# Hosted Security Model

> Status: design. Target: ADR-0025 at hosted launch.

## Threat model summary

| ID | Threat | Likelihood | Impact | Mitigation |
|----|---|---|---|---|
| H1 | Adapter key exfiltrated from server disk | Medium | High | KMS-wrapped DEK; plaintext key never on disk |
| H2 | Tenant volume read by another tenant | Low | Critical | Physical isolation: 1 container, 1 volume per tenant |
| H3 | Compromised provider key used to drain founder's bank | Medium | Critical | Adapter keys are read-only by vendor convention; founder rotates on alert |
| H4 | Cockpit XSS injects edit on proposal branch | Low | High | Frontend escapes; backend re-validates path + policy; CSP strict |
| H5 | Backup bucket read by hosted operator | Medium | Critical | Founder owns the bucket; we hold a write-only role; restores are founder-initiated |
| H6 | Insider with prod access reads tenant repo | Low | Critical | Repo encrypted at rest with per-tenant DEK; operator access logged + signed |

Likelihood/impact are coarse — refine post-launch.

## Encryption at rest

- Each tenant volume is backed by a per-tenant **Data Encryption Key (DEK)**.
- The DEK is wrapped by the tenant's **Key Encryption Key (KEK)** in
  AWS KMS (or GCP Cloud KMS — cloud-agnostic via envelope encryption).
- The wrapped DEK lives in the tenant control-plane row; the
  unwrapped DEK lives only in container memory after KMS Decrypt.
- KMS Decrypt is gated on a tenant-id IAM condition; one tenant's
  IAM role cannot decrypt another tenant's DEK.

## Secret store for adapter keys

- Founder enters keys via the cockpit Settings page or
  `lycato keys set` (which forwards to a hosted endpoint over TLS).
- Keys are encrypted with the tenant DEK before write.
- Stored at `secrets/<name>.enc` in the tenant volume — never on
  control-plane storage.
- Decrypted in-memory at adapter-run time only; never logged.
- Rotation: founder writes a new value; old is overwritten. Audit
  log records `{ name, action: rotated, at, by }` — never the value.

## Network posture

- All inbound is TLS 1.2+ via Caddy on the edge.
- The cockpit Fastify server binds 127.0.0.1 inside the container;
  only Caddy can reach it.
- Inter-container traffic on a Docker network with `--internal`
  flag (no host network bridge).
- Egress: per-tenant container can reach vendor APIs (Mercury,
  Stripe, …) and the LLM provider only. Outbound firewall enforces
  an allowlist.

## Authentication boundary

- See `AUTH.md`. Summary: passkey + magic-link, no passwords.
- Session cookies are `HttpOnly; Secure; SameSite=Lax`.
- Sessions are bound to a tenant id. Cross-tenant requests get
  a 404, not a 403, to avoid tenant-existence leaks.

## Audit

- Every `POST /api/file` write, every approval, every secret
  rotation appends to `.founder/audit/YYYY-MM.jsonl` in the tenant
  volume. Founder-readable, never operator-rewritable.
- Operator actions (container restart, backup restore) append to
  a SEPARATE control-plane audit log + emit to the tenant's audit
  log so the founder sees them too.

## What's intentionally absent

- HSM-grade key custody. KMS is sufficient for the founder
  segment; HSM enters scope at enterprise tier (post v1.0).
- Per-document encryption. Volume-level DEK is the right granularity
  for the cost / complexity ratio.
- Customer-managed keys (BYOK). Possible in v1.x; not at launch.
