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:
Moheeb Zara
2026-05-16 20:31:43 -07:00
parent 1cbea138ba
commit 59e9fcbd4f
17 changed files with 1203 additions and 11 deletions
+43
View File
@@ -903,6 +903,49 @@ export const listMemberCardsSchema = z.object({
memberId: z.string().uuid('Invalid member ID'),
})
// ─── Door connections (Door epic P2) ─────────────────────────────────────────
const httpUrl = z
.string()
.trim()
.max(2000)
.regex(/^https?:\/\/.+/i, 'Must be an http(s) URL')
const doorAdapter = z.enum(['native_heatsync', 'generic_http'])
const doorAuthMode = z.enum(['none', 'query', 'header', 'bearer'])
export const createDoorConnectionSchema = z.object({
name: z.string().min(1, 'Name is required').max(200),
adapter: doorAdapter.optional().default('generic_http'),
base_url: httpUrl,
pinned_host: z.string().min(1, 'A pinned host is required').max(255),
auth_mode: doorAuthMode.optional().default('none'),
auth_param: z.string().max(120).optional().nullable(),
secret_ref: z.string().uuid('Invalid secret reference').optional().nullable(),
// generic adapter per-verb templates (validated as a flat string map)
verbs: z.record(z.string().max(2000)).optional().default({}),
allow_member_self_entry: z.boolean().optional().default(false),
is_enabled: z.boolean().optional().default(true),
})
export const updateDoorConnectionSchema = z.object({
connectionId: z.string().uuid('Invalid connection ID'),
name: z.string().min(1).max(200).optional(),
adapter: doorAdapter.optional(),
base_url: httpUrl.optional(),
pinned_host: z.string().min(1).max(255).optional(),
auth_mode: doorAuthMode.optional(),
auth_param: z.string().max(120).optional().nullable(),
secret_ref: z.string().uuid('Invalid secret reference').optional().nullable(),
verbs: z.record(z.string().max(2000)).optional(),
allow_member_self_entry: z.boolean().optional(),
is_enabled: z.boolean().optional(),
})
export const doorConnectionIdSchema = z.object({
connectionId: z.string().uuid('Invalid connection ID'),
})
// ─── Generic ID schemas ──────────────────────────────────────────────────────
export const uuidSchema = z.string().uuid('Invalid ID format')