feat(db): owner_secret_key + root_key columns on mesh for server-side signing
Completes the server-side invite-signing story. The web UI's create-invite flow needs the mesh owner's ed25519 SECRET key to sign each invite payload; these columns let the backend hold + use them per mesh. - mesh.mesh.owner_secret_key (text, nullable): ed25519 secret key (hex, 64 bytes) paired with owner_pubkey. Stored PLAINTEXT AT REST for v0.1.0. Acceptable trade-off for a managed-broker SaaS launch — the operator controls the key anyway. v0.2.0 will either encrypt with a column-level KEK or migrate to client-held keys. - mesh.mesh.root_key (text, nullable): 32-byte shared key (base64url, no padding) used by channel/broadcast encryption in later steps. Embedded in every invite so joiners receive it at join time. migrations/0002_vengeful_enchantress.sql — two ALTER TABLE ADD COLUMN. Nullable so existing rows don't need backfill to migrate; the backfill script populates them idempotently. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2
packages/db/migrations/0002_vengeful_enchantress.sql
Normal file
2
packages/db/migrations/0002_vengeful_enchantress.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "mesh"."mesh" ADD COLUMN "owner_secret_key" text;--> statement-breakpoint
|
||||
ALTER TABLE "mesh"."mesh" ADD COLUMN "root_key" text;
|
||||
2833
packages/db/migrations/meta/0002_snapshot.json
Normal file
2833
packages/db/migrations/meta/0002_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,13 @@
|
||||
"when": 1775339743477,
|
||||
"tag": "0001_demonic_karnak",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "7",
|
||||
"when": 1775340519054,
|
||||
"tag": "0002_vengeful_enchantress",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -89,6 +89,23 @@ export const mesh = meshSchema.table("mesh", {
|
||||
* rows; required for new meshes.
|
||||
*/
|
||||
ownerPubkey: text(),
|
||||
/**
|
||||
* ed25519 secret key (hex, 64 bytes) that signs invites server-side.
|
||||
*
|
||||
* v0.1.0: stored plaintext-at-rest. Acceptable trade-off for a
|
||||
* managed-broker SaaS launch — the operator controls the key.
|
||||
* v0.2.0 will either (a) encrypt-at-rest with a column-level KEK,
|
||||
* or (b) migrate to client-held keys so the server never holds
|
||||
* admin material.
|
||||
*/
|
||||
ownerSecretKey: text(),
|
||||
/**
|
||||
* 32-byte shared key (base64url) used by channels/broadcasts in the
|
||||
* mesh. Embedded in invites so joiners can encrypt/decrypt channel
|
||||
* traffic. Not used by 1:1 direct messages (those use crypto_box
|
||||
* with recipient's ed25519 pubkey).
|
||||
*/
|
||||
rootKey: text(),
|
||||
createdAt: timestamp().defaultNow().notNull(),
|
||||
archivedAt: timestamp(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user