next: service-device records and role-0 log tokens (cloud-copy bootstrap C1) - #615
Conversation
…lections Add next_service_devices (0048): hosted/escrow members of cloud_copy collections only, via the (collection_id, sync) composite key. The control plane stores public keys and the deployment's KMS-wrapped keys and never unwraps them. - service-devices.ts: strict record parsing (canonical 32-byte hex keys, canonical base64 wrapped keys <= 64 KiB, arn), idempotent put-first store that refuses a different device, lookup that requires a current cloud copy under a share lock, and a bounded HTTPS-only generate client (no redirects, timeout, 128 KiB response cap) for the C2 bootstrap. - hosted-routes.ts: GET collections/:id/service-devices/:kind and POST service-devices/:device/log-token, kind-matched to the caller's internal token, 409 unless standard. Tokens are role 0, name the device, its sign key and one collection, and last 15 minutes.
…mint; CHECK key and wrapped sizes; validate every writer Review (control): fetch and mint now run in one transaction under FOR SHARE of the collection row, so a concurrent leave waits for the mint and a later fetch/mint sees it. 0048 CHECKs 32-byte keys and 1..64 KiB wrapped keys (pg-mem gains octet_length); storeServiceDevice validates records from any writer. Deployments need not be idempotent: the CP's first stored record wins.
|
Control's review is addressed in 12b2c9d.
Results: server 784 passed; next/* 155 passed against PG16. |
|
Control touched-path/source owner ACK at exact C1 head 12b2c9d. Independently reviewed initial record/generator/SQL/routes/app source and the 93d1a31→12b2c9d9 delta, including the memory-only octet_length shim and new PostgreSQL regression source. The two owner findings are closed in source: writer revalidation plus database key/blob bounds; real BEGIN/COMMIT with bounded lock wait retaining the collection share lock through record materialization/token mint. Kind-matched authentication, current standard/not-left lookup, exact role-0 single-collection issuance, put-first/no replacement and no CP decrypt remain intact. This is source review only: I did not rerun author tests, exercise LAB, verify IAM/material, or grant runtime/Ready clearance. C2 #616 is separate; scoped security and actual CI qualification remain required. |
…-zero noise_pk except for recovery)
|
Control delta-only source owner ACK at f3076cd (12b2c9d→f3076cde, three paths reviewed). Parser now rejects nil device IDs and weak/all-zero Noise keys for both hosted and escrow; test source follows. This matches coordinator 15:10: escrow enrols a genuine unused Noise static key; no policy-validator change or Noise endpoint. Prior two owner fixes carry. This closes the reported zero-Noise mismatch in CP source only, not actual escrow generation/wrapping, policy-validator execution, IAM, LAB or epoch/Ready acceptance. Scoped security and CI remain required. |
|
security: no blocking findings at f3076cd, base 6251060 (scoped C1 service-device records and credential routes). Reviewed all 9 changed paths: CloudCopy-only FK, strict bounded/public-key-and-KMS-ciphertext parsing, immutable first-store/idempotence, current-parent lock through record serving/token mint, deployment-kind separation, and HTTPS/no-redirect/bounded generation client. Genuine unused escrow Noise key and non-nil device ID now match the unchanged policy rule; no escrow Noise endpoint is authorized. Independently executed 26 hermetic actual-parser/generator/HTTP-route/token-crypto checks plus a SQL query model: PASS, not independently executed PostgreSQL/FK/lock tests. Author PG/suites, source ownership and CI are separate. Role-0 exact-collection 15-minute credential only; no CP plaintext epoch/private-key authority. This is not KMS/IAM custody, service PoP, genesis-to-Replica/key-delivery/Ready, mutable consumers, private conversion, live or deployment clearance. Superseded owner-only CloudCopy restrictions are not applied. |
Cloud-copy bootstrap, PR C1 of 2 (mdbase-next interface note 2026-10-04-control-hosted-replica.md §2/§3). Control owns these paths and ACKed the plan (14:21). Security-2 review requested.
What this does
cloud_copycollections: the childsynccolumn is pinned tocloud_copythrough the(collection_id, sync)composite key from 0038.PRIMARY KEY (collection_id, kind);device_idis globally unique.arn:string, and no extra fields.ON CONFLICT DO NOTHING, then re-read and compare. A different record for the same collection and kind gets a 409 and is never replaced.cloud_copyandleft_sync_at IS NULL. This is checked in the same statement underFOR SHARE OF parent.generateServiceDevice:redirect: "error", 10 s timeout;GET /internal/v1/next/collections/:id/service-devices/:kindreturns the record to the deployment whose internal token matches that kind.POST /internal/v1/next/service-devices/:device/log-token {collection}returns{token, expires_at}fromLogServiceClient.mintToken. The token is role 0, carries the device id, the record'ssign_pkand claim 5 = that one collection, and lastsnow + 15 min.LogServiceClientbetween the device and hosted routes.Not in this PR (C2)
Tests
service-devices.test.ts(hermetic): parse round-trip and rejections; generate checks HTTPS, headers, body, redirect mode, kind, size cap and error mapping.service-devices.postgres.test.ts:pnpm test(server): 783 passed;src/features/nextagainst a dedicated Postgres 16,--no-file-parallelism: 151 passed;check:architecture(with justification) andcheck:changelogpass.