01Fail-closed tenancy
Two Postgres roles, and I deliberately made the request-serving one not the table owner, not a superuser and not BYPASSRLS, so it has no route around RLS. The policy reads tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::uuid: on a pooled connection where the setting was never applied, the empty string coerces to NULL and the comparison returns no rows instead of raising an invalid-uuid error. It fails to silence rather than to a leak — run it locally and the app role sees zero tenants where the owner role sees two.
02Read-only forensics
I audited the Bitrix portal over SSH under one hard rule: SELECT, SHOW and DESCRIBE, never a write to a client's live database. It produced about seventy artefacts, 45 analysis documents plus 24 raw table dumps, including the permission scope ladder I recovered from source (A=Self, D=Dept, F=Dept+subs, O=Open, X=All) and a decoded 9,628-row role-permission matrix. The most useful finding was that the CRM robot board is deprecated and delegates to the bizproc workflow runtime, which settled the design: one durable runtime with two editors over it, not two engines.
03Agents under RBAC
An AI teammate is not a special case in my authorisation code. It is a row in users with is_ai_agent=true, so the same principal loader resolves it, RLS scopes it, and role grants bind every action it takes. A new agent starts with no grants and can see and do nothing until an admin assigns roles; anything the guardrails mark require_human lands in a pending_agent_actions queue instead of executing.
04At-least-once outbox
State change and event row commit in one transaction, and a dispatcher drains them with FOR UPDATE SKIP LOCKED so several instances can run at once without double-publishing or blocking each other. Publish happens before the published_at stamp commits, so delivery is at-least-once by choice. I wrote that trade-off into the decision log with its reason: no-loss beats no-duplication, and exactly-once across a publish and a database commit is not reachable without idempotent consumers anyway.
05Deploying without root
The target box already ran seven other projects and my deploy user had no passwordless sudo, so I put everything into userspace: a dedicated Postgres cluster on port 5433 bound to localhost, free high ports for API and web, pm2 with pm2 save for restart-on-boot. On the server the Next config self-proxies /v1 to the API on 127.0.0.1 with NEXT_PUBLIC_API_URL left empty, so the same build works on a bare address now and behind a domain later with no rebuild, no CORS and no mixed content. I left the seven existing apps untouched and their uptimes unchanged.