feat: add machine data encryption key and new-machine update
This commit is contained in:
parent
31bb2892f2
commit
d4570c6b8f
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Machine" ADD COLUMN "dataEncryptionKey" BYTEA;
|
@ -199,6 +199,7 @@ model Machine {
|
||||
metadataVersion Int @default(0)
|
||||
daemonState String? // Encrypted - contains dynamic daemon state
|
||||
daemonStateVersion Int @default(0)
|
||||
dataEncryptionKey Bytes?
|
||||
seq Int @default(0)
|
||||
active Boolean @default(true)
|
||||
lastActiveAt DateTime @default(now())
|
||||
|
@ -5,7 +5,7 @@ import { db } from "@/storage/db";
|
||||
import { log } from "@/utils/log";
|
||||
import { randomKeyNaked } from "@/utils/randomKeyNaked";
|
||||
import { allocateUserSeq } from "@/storage/seq";
|
||||
import { buildUpdateMachineUpdate } from "@/app/events/eventRouter";
|
||||
import { buildNewMachineUpdate, buildUpdateMachineUpdate } from "@/app/events/eventRouter";
|
||||
|
||||
export function machinesRoutes(app: Fastify, eventRouter: EventRouter) {
|
||||
app.post('/v1/machines', {
|
||||
@ -14,12 +14,13 @@ export function machinesRoutes(app: Fastify, eventRouter: EventRouter) {
|
||||
body: z.object({
|
||||
id: z.string(),
|
||||
metadata: z.string(), // Encrypted metadata
|
||||
daemonState: z.string().optional() // Encrypted daemon state
|
||||
daemonState: z.string().optional(), // Encrypted daemon state
|
||||
dataEncryptionKey: z.string().nullish()
|
||||
})
|
||||
}
|
||||
}, async (request, reply) => {
|
||||
const userId = request.userId;
|
||||
const { id, metadata, daemonState } = request.body;
|
||||
const { id, metadata, daemonState, dataEncryptionKey } = request.body;
|
||||
|
||||
// Check if machine exists (like sessions do)
|
||||
const machine = await db.machine.findFirst({
|
||||
@ -39,6 +40,7 @@ export function machinesRoutes(app: Fastify, eventRouter: EventRouter) {
|
||||
metadataVersion: machine.metadataVersion,
|
||||
daemonState: machine.daemonState,
|
||||
daemonStateVersion: machine.daemonStateVersion,
|
||||
dataEncryptionKey: machine.dataEncryptionKey ? Buffer.from(machine.dataEncryptionKey).toString('base64') : null,
|
||||
active: machine.active,
|
||||
activeAt: machine.lastActiveAt.getTime(), // Return as activeAt for API consistency
|
||||
createdAt: machine.createdAt.getTime(),
|
||||
@ -57,19 +59,30 @@ export function machinesRoutes(app: Fastify, eventRouter: EventRouter) {
|
||||
metadataVersion: 1,
|
||||
daemonState: daemonState || null,
|
||||
daemonStateVersion: daemonState ? 1 : 0,
|
||||
dataEncryptionKey: dataEncryptionKey ? Buffer.from(dataEncryptionKey, 'base64') : undefined,
|
||||
// Default to offline - in case the user does not start daemon
|
||||
active: false,
|
||||
// lastActiveAt and activeAt defaults to now() in schema
|
||||
}
|
||||
});
|
||||
|
||||
// Emit update for new machine
|
||||
const updSeq = await allocateUserSeq(userId);
|
||||
// Emit both new-machine and update-machine events for backward compatibility
|
||||
const updSeq1 = await allocateUserSeq(userId);
|
||||
const updSeq2 = await allocateUserSeq(userId);
|
||||
|
||||
// Emit new-machine event with all data including dataEncryptionKey
|
||||
const newMachinePayload = buildNewMachineUpdate(newMachine, updSeq1, randomKeyNaked(12));
|
||||
eventRouter.emitUpdate({
|
||||
userId,
|
||||
payload: newMachinePayload
|
||||
});
|
||||
|
||||
// Emit update-machine event for backward compatibility (without dataEncryptionKey)
|
||||
const machineMetadata = {
|
||||
version: 1,
|
||||
value: metadata
|
||||
};
|
||||
const updatePayload = buildUpdateMachineUpdate(newMachine.id, updSeq, randomKeyNaked(12), machineMetadata);
|
||||
const updatePayload = buildUpdateMachineUpdate(newMachine.id, updSeq2, randomKeyNaked(12), machineMetadata);
|
||||
eventRouter.emitUpdate({
|
||||
userId,
|
||||
payload: updatePayload
|
||||
@ -82,6 +95,7 @@ export function machinesRoutes(app: Fastify, eventRouter: EventRouter) {
|
||||
metadataVersion: newMachine.metadataVersion,
|
||||
daemonState: newMachine.daemonState,
|
||||
daemonStateVersion: newMachine.daemonStateVersion,
|
||||
dataEncryptionKey: newMachine.dataEncryptionKey ? Buffer.from(newMachine.dataEncryptionKey).toString('base64') : null,
|
||||
active: newMachine.active,
|
||||
activeAt: newMachine.lastActiveAt.getTime(), // Return as activeAt for API consistency
|
||||
createdAt: newMachine.createdAt.getTime(),
|
||||
@ -109,6 +123,7 @@ export function machinesRoutes(app: Fastify, eventRouter: EventRouter) {
|
||||
metadataVersion: m.metadataVersion,
|
||||
daemonState: m.daemonState,
|
||||
daemonStateVersion: m.daemonStateVersion,
|
||||
dataEncryptionKey: m.dataEncryptionKey ? Buffer.from(m.dataEncryptionKey).toString('base64') : null,
|
||||
seq: m.seq,
|
||||
active: m.active,
|
||||
activeAt: m.lastActiveAt.getTime(),
|
||||
@ -147,6 +162,7 @@ export function machinesRoutes(app: Fastify, eventRouter: EventRouter) {
|
||||
metadataVersion: machine.metadataVersion,
|
||||
daemonState: machine.daemonState,
|
||||
daemonStateVersion: machine.daemonStateVersion,
|
||||
dataEncryptionKey: machine.dataEncryptionKey ? Buffer.from(machine.dataEncryptionKey).toString('base64') : null,
|
||||
seq: machine.seq,
|
||||
active: machine.active,
|
||||
activeAt: machine.lastActiveAt.getTime(),
|
||||
|
@ -80,6 +80,19 @@ export type UpdateEvent = {
|
||||
version: number;
|
||||
} | null | undefined;
|
||||
github?: GitHubProfile | null | undefined;
|
||||
} | {
|
||||
type: 'new-machine';
|
||||
machineId: string;
|
||||
seq: number;
|
||||
metadata: string;
|
||||
metadataVersion: number;
|
||||
daemonState: string | null;
|
||||
daemonStateVersion: number;
|
||||
dataEncryptionKey: string | null;
|
||||
active: boolean;
|
||||
activeAt: number;
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
} | {
|
||||
type: 'update-machine';
|
||||
machineId: string;
|
||||
@ -349,6 +362,40 @@ export function buildUpdateAccountUpdate(userId: string, profile: Partial<Accoun
|
||||
};
|
||||
}
|
||||
|
||||
export function buildNewMachineUpdate(machine: {
|
||||
id: string;
|
||||
seq: number;
|
||||
metadata: string;
|
||||
metadataVersion: number;
|
||||
daemonState: string | null;
|
||||
daemonStateVersion: number;
|
||||
dataEncryptionKey: Uint8Array | null;
|
||||
active: boolean;
|
||||
lastActiveAt: Date;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}, updateSeq: number, updateId: string): UpdatePayload {
|
||||
return {
|
||||
id: updateId,
|
||||
seq: updateSeq,
|
||||
body: {
|
||||
t: 'new-machine',
|
||||
machineId: machine.id,
|
||||
seq: machine.seq,
|
||||
metadata: machine.metadata,
|
||||
metadataVersion: machine.metadataVersion,
|
||||
daemonState: machine.daemonState,
|
||||
daemonStateVersion: machine.daemonStateVersion,
|
||||
dataEncryptionKey: machine.dataEncryptionKey ? Buffer.from(machine.dataEncryptionKey).toString('base64') : null,
|
||||
active: machine.active,
|
||||
activeAt: machine.lastActiveAt.getTime(),
|
||||
createdAt: machine.createdAt.getTime(),
|
||||
updatedAt: machine.updatedAt.getTime()
|
||||
},
|
||||
createdAt: Date.now()
|
||||
};
|
||||
}
|
||||
|
||||
export function buildUpdateMachineUpdate(machineId: string, updateSeq: number, updateId: string, metadata?: { value: string; version: number }, daemonState?: { value: string; version: number }): UpdatePayload {
|
||||
return {
|
||||
id: updateId,
|
||||
|
@ -64,6 +64,19 @@ declare global {
|
||||
version: number;
|
||||
} | null | undefined;
|
||||
github?: GitHubProfileType | null | undefined;
|
||||
} | {
|
||||
t: 'new-machine';
|
||||
machineId: string;
|
||||
seq: number;
|
||||
metadata: string;
|
||||
metadataVersion: number;
|
||||
daemonState: string | null;
|
||||
daemonStateVersion: number;
|
||||
dataEncryptionKey: string | null;
|
||||
active: boolean;
|
||||
activeAt: number;
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
} | {
|
||||
t: 'update-machine';
|
||||
machineId: string;
|
||||
|
Loading…
Reference in New Issue
Block a user