# Security & data isolation

Every request is checked the same way: who is calling, which workspace, what their role allows, and whether the resource belongs to that workspace's project. An id from another workspace answers 404 before any work happens.

## Your account

- Two-factor sign-in: Settings → Account. Scan the code with an authenticator app, confirm a code, and keep the recovery codes. Sign-in then asks for a code (the API answers `{ mfaRequired, ticket }`; complete with `POST /api/auth/login/mfa { ticket, code | recoveryCode }`). Turning it off needs your password and a code.
- Changing your email needs your current password (and a code when two-factor is on), then a code sent to the new address. The old address is told.
- A password reset or change signs out your other sessions and disconnects AI connectors.
- Sessions last 30 days; Settings lists them and signs the others out.
- Changes made with the dashboard cookie are refused when they come from another site.

## Keys and connectors

- API keys can be read-only, limited to one project, or both (see API keys & authentication). Give each app the narrowest key that works.
- AI connectors are connected read-only or read-and-manage; a read-only connector cannot change anything. Changes still ask you first in the assistant.
- No key or connector can change your email, password or sessions, accept invitations, or move money out — those need you in a browser.
- Revoke keys in Settings → API access and connectors in Dashboard → Connectors; both take effect immediately.

## How your data is kept apart

| Layer | Separation |
| --- | --- |
| Every API call | Your credential resolves to one workspace; resources are found through project → workspace, never by id alone. |
| Databases | Each database is its own SQLite database. SQL in one can never reach another. |
| Storage | Each bucket is its own bucket in object storage; a signed URL is for one key in one bucket. |
| Vector DB | One index per collection. |
| Memory | Rows in Harakumo's control database, keyed by store and scope; searches never cross scopes. |
| Functions and static sites | Each runs as its own isolated edge program with only its project's environment variables and no access to platform storage. |
| Server apps | Each deployment runs in its own container. |
| Auth pools | One per project, with its own signing secret. |
| Payments | Buyers pay into Harakumo's processor account; each project's share is tracked in a ledger, and withdrawals go only to the payout account the workspace owner connected. |
| Mail | Each workspace sends from no-reply@harakumo.com or from its own verified domains (a domain belongs to one workspace), with its own log and suppression list. |
| Domains | Each root domain has one owning workspace; no other workspace can add a hostname under it. |

## How secrets are stored

- Passwords (yours and your end users'): PBKDF2 hashes.
- Sessions, API keys, connector tokens and auth-pool client secrets: SHA-256 hashes; the plaintext is shown once.
- Two-factor recovery codes: hashed, and each works once.
- Environment variables and auth-pool JWT secrets: stored readable, because they are injected into deploys and used to sign tokens. Encryption at rest is planned.

## Reference

**REST**

```http
POST /api/auth/login           { "email", "password" }        → session, or { mfaRequired, ticket }
POST /api/auth/login/mfa       { "ticket", "code" | "recoveryCode" }
GET|POST|PUT|PATCH|DELETE /api/auth/mfa   # status, set up, confirm, new recovery codes, turn off (browser session)
GET  /api/auth/sessions · DELETE                                # sign out other sessions
GET  /api/security/overview    # the workspace's security checks
```
