feat(door): P2 connections + SSRF-safe executor + native adapter + audit
Migration 035 adds door_connections (per-space controller integration: secret_ref -> AES-256-GCM secrets vault, never a plaintext password; pinned_host SSRF pin; native_heatsync|generic_http adapter; allow_member_self_entry opt-in for phase 3) and door_access_log (append-only, secrets redacted, service-client-only writes; immutable). RLS additive/default-deny: connections CRUD = door.manage; log SELECT = door.manage/operate. Safety core is pure and heavily unit-tested (door-logic, suite 409): validateDoorTarget (request host must equal the pinned host; metadata/link-local always blocked; http(s) only), the firmware-verified HeatSync encoders with fixed-width zero-padding (slot 3 / perm 3 / tag 8 / 4-char password), generic template substitution, and redactDoorSecrets. lib/door/executor.ts is the single hardened egress: re-validates the pin, refuses redirects, caps time and body, redacts before any audit write. The door password is read from the secrets vault and decrypted server-side only, never returned or logged. /door/manage (door.manage) configures connections, picks a vault secret, and runs a safe status-only test. Sidebar Admin link added; "Learn" moved before "People" per request. Docs updated.
This commit is contained in:
@@ -928,3 +928,7 @@ Registry management is gated by `equipment.manage`; reserving needs only members
|
||||
- `reserveEquipment(input)` / `cancelReservation(input)` — any member. Validated service-client writes (`equipment_reservations` has no INSERT/DELETE policy): `reserveEquipment` runs `reservationEligibility` (status/active, future window, `hasConflict` no-overlap, required-cert) — a holder of `equipment.manage` gets the override and may pass `memberId` to book on someone's behalf; `cancelReservation` allows the owner or an `equipment.manage` holder.
|
||||
- `listEquipmentReservations(input)` — `equipment.manage`; reservations for one item (joined member) for the schedule view.
|
||||
- `getMyReservations()` — the signed-in member's own reservations (joined equipment). No params; RLS restricts to own rows (the `/me` view).
|
||||
|
||||
### Door epic: `member-cards.ts` (migration 034) + `door.ts` (migration 035)
|
||||
Card UID is a credential. `member-cards.ts`: `addMemberCard`/`updateMemberCard`/`deleteMemberCard`/`listMemberCards` are `door.manage` (full UID); `getMyCards()` is the member's own MASKED view (service client, returns only count + last4, never the raw UID — `member_cards` has no member RLS SELECT).
|
||||
`door.ts` (all `door.manage` except the read log): `createDoorConnection`/`updateDoorConnection`/`deleteDoorConnection`/`listDoorConnections`; `listSecretTitles()` (titles only, to pick a vault secret for the password). `testDoorConnection(input)` runs only the `status` verb through the hardened executor and writes one redacted audit row. `listDoorAccessLog()` is `door.manage` OR `door.operate`. Security model: the shared password is read from the AES-256-GCM secrets vault server-side and embedded in the request per the verified firmware (auth is a query param); `lib/door/executor.ts` is the single egress — it enforces `validateDoorTarget` (request host must equal the connection's pinned host; metadata/link-local always blocked; http(s) only), refuses redirects, caps time/body, and `redactDoorSecrets` scrubs the password before anything is logged. Native HeatSync verbs are encoded with the verified fixed-width zero-padding (slot 3 / perm 3 / tag 8 / `&e=` password); generic connections use admin `{slot}/{tag}/{pw}` templates. Live grant/revoke/open and the per-connection member self-entry are phase 3.
|
||||
|
||||
Reference in New Issue
Block a user