# Service Account Model (Pre-Implementation Baseline)

## Purpose
Define machine identity and authorization shape before service-account coding begins.
This document is contract-first guidance for the upcoming implementation phase.

## Scope
- Service account ownership and lifecycle.
- Auth token model.
- Authorization boundaries.
- Audit and security controls.
- Minimal schema contract.

## Core Principles
1. Service accounts are non-human identities scoped to a single project.
2. Service accounts inherit tenant boundary through project ownership.
3. Tokens minted for service accounts are short-lived and audience-scoped.
4. Service-account actions are fully auditable with identity attribution.
5. No static long-lived bearer secrets in application code or UI.

Important current limitation:
- this baseline is sufficient for project-scoped app controllers,
- it is not yet the final model for tenant-owned shared app runtimes,
- those require a tenant-bounded machine-identity model that does not widen into
  platform-global authority,
- see:
  - `doc/architecture/App_Tenant_Shared_Attachment_Model_v1.md`
  - `doc/architecture/IAM_Token_Issuer_v1.md`

## Ownership Model
```text
Tenant (org)
  -> Project
    -> Service Account
```

Rules:
1. `service_accounts.project_id` is required.
2. `service_accounts.org_id` is required (denormalized to avoid project-join fanout on every policy/billing/audit query; consistency enforced by project/org FK invariant).
3. Cross-project use is not allowed; access is project-scoped.
4. Cross-tenant use is forbidden.

## Minimal Schema Shape (Planned)

### service_accounts
- `id uuid pk`
- `org_id uuid not null references organizations(id)`
- `project_id uuid not null references projects(id)`
- `name text not null`
- `slug text not null`
- `description text null`
- `state text not null check (state in ('active','disabled','deleted'))`
- `created_by_user_id uuid not null references users(id)`
- `created_at timestamptz not null default now()`
- `disabled_at timestamptz null`
- `deleted_at timestamptz null`

Constraints:
- unique (`project_id`, `slug`)
- invariant: `project.org_id == service_accounts.org_id`

### service_account_credentials
- `id uuid pk`
- `service_account_id uuid not null references service_accounts(id)`
- `key_id text not null`
- `public_jwk jsonb not null`
- `private_key_enc jsonb not null`
- `algorithm text not null` (MVP: `RS256` or `EdDSA`, choose one in implementation ADR)
- `state text not null check (state in ('active','rotated','revoked'))`
- `created_at timestamptz not null default now()`
- `expires_at timestamptz null`
- `revoked_at timestamptz null`

Constraints:
- unique (`service_account_id`, `key_id`)
- at most one active primary credential per service account in MVP

## Auth Model (MVP)
1. Human users authenticate with OIDC as today.
2. Service accounts use a dedicated machine-token endpoint (client-credentials style).
3. Machine access token claims include:
   - `sub`: service account id
   - `actor_type`: `service_account`
   - `org_id`
   - `project_id`
   - `scope` / `permissions`
   - `exp`, `iat`, `iss`, `aud`, `jti`
4. Token TTL is short (default 15m; policy-controlled).
5. Refresh tokens are not issued for service accounts in MVP.

Follow-on consolidation:
- `doc/architecture/IAM_Token_Issuer_v1.md` defines the shared issuer model for
  service-account tokens, delegated shared-runtime operator tokens, future
  workload-bound tokens, and provider credential brokering.
- Service-account endpoint contracts remain stable; the issuer is an internal
  issuer/policy/audit consolidation, not a new broad public token endpoint.

## Authorization Model
1. Requests from service-account tokens are evaluated against project-scoped permissions.
2. Service accounts cannot call admin-only endpoints.
3. Service accounts cannot manage IAM/billing/tenant settings.
4. Endpoint allowlist for service accounts must be explicit in contract docs.
5. `ownership_required` checks resolve by project scope in token claims.

MVP allowlist (implemented):
- `GET /api/v1/skus`
- `GET /api/v1/nodes`
- `GET /api/v1/projects/{project_id}/app-instances`
- `POST /api/v1/projects/{project_id}/app-instances`
- `GET /api/v1/projects/{project_id}/app-instances/{app_instance_id}`
- `DELETE /api/v1/projects/{project_id}/app-instances/{app_instance_id}`
- `POST /api/v1/projects/{project_id}/app-instances/{app_instance_id}/upgrade`
- `POST /api/v1/projects/{project_id}/app-instances/{app_instance_id}/rollback`
- `POST /api/v1/projects/{project_id}/app-instances/{app_instance_id}/decommission`
- `POST /api/v1/projects/{project_id}/access-credentials/{credential_id}/deliver`
- `GET /api/v1/storage/list`
- `GET /api/v1/storage/download`
- `PUT /api/v1/storage/upload`
- `POST /api/v1/storage/mkdir`
- `POST /api/v1/storage/rename`
- `DELETE /api/v1/storage/delete`

For project-scoped app-instance endpoints, `{project_id}` must equal token claim `project_id`.
For storage endpoints, `X-Project-ID` is required and must equal token claim `project_id`.
Any non-allowlisted endpoint returns `403 insufficient_permissions`.

## API Surface (Planned)
Tenant/project admin managed endpoints:
1. `POST /api/v1/projects/{project_id}/service-accounts`
2. `GET /api/v1/projects/{project_id}/service-accounts`
3. `POST /api/v1/projects/{project_id}/service-accounts/{id}/disable`
4. `POST /api/v1/projects/{project_id}/service-accounts/{id}/rotate-key`
5. `DELETE /api/v1/projects/{project_id}/service-accounts/{id}` (soft delete)

Token issuance endpoint:
1. `POST /api/v1/auth/service-account/token`

## Audit Requirements
Every service-account lifecycle mutation must write `platform_audit_logs` with:
- actor (human admin user),
- action (`service_account.create|disable|rotate|delete`),
- target (`service_account` id),
- result,
- `correlation_id`.

Every machine action should log actor context:
- `actor_type=service_account`
- `actor_id=<service_account_id>`
- `org_id`, `project_id`
- `correlation_id`.

## App Runtime Credential Operations

Project-scoped service accounts are allowed to call app runtime credential
reconcile, rotate, disable, and sweep operations for workloads in their bound
project. The API accepts these routes only through the service-account
allowlist, then resolves `X-Project-ID` against the token `project_id` before
calling inventory/runtime services.

Rules:
1. `X-Project-ID` is required.
2. `X-Project-ID` must equal the service-account token `project_id`.
3. Cross-project requests fail before inventory/runtime service calls.
4. Responses are operation evidence only and never include raw credential
   material, wrapped tokens, private keys, passwords, or provider secrets.

## Security Controls
1. Private keys for service-account credentials are always stored encrypted (`*_enc` envelope format).
2. Rotation is mandatory and policy-driven.
3. Revocation must be immediate (deny-list by `jti` or credential state check).
4. Tokens never via query parameters.
5. Scope minimization: per-service-account endpoint permissions only.

## MVP Key Custody Assumption
1. MVP allows encrypted private-key material to be stored in Postgres (`private_key_enc`).
2. Production hardening target is external key custody (KMS/Vault reference model) with rotation and revocation parity.
3. This assumption is tracked in `doc/governance/Assumptions_Register.md` (A-019).
4. Vault-first custody baseline is defined in `doc/architecture/Platform_Vault_Secrets_Baseline_v1.md`.

## Policy Keys (Planned)
1. `auth.service_account_token_ttl_seconds`
2. `auth.service_account_max_active_keys`
3. `auth.service_account_rotation_days`
4. `auth.service_account_allowed_scopes` (json)

## Non-Goals (Initial Rollout)
1. Cross-project or cross-tenant service accounts.
2. Service-account UI in MVP if API-first rollout is sufficient.
3. External IdP-managed machine identities.

Follow-on note:
- tenant-owned shared runtimes may require tenant-scoped machine identity or an
  equivalent delegated model, but that should be added as an explicit next
  contract rather than weakening the project-scoped baseline in place.

## Acceptance Gate Before Coding
1. OpenAPI endpoints and token schema added.
2. Async/event contracts added if lifecycle events are emitted.
3. ERD and db schema include service account tables/constraints.
4. Authorization matrix updated for actor_type = service_account.
5. Security controls mapped in `Security_Control_Verification.md`.

## References
- `doc/architecture/Tenant_Project_Ownership_Baseline.md`
- `doc/architecture/adrs/ADR-004-identity-authz-model.md`
- `doc/governance/Security_Control_Verification.md`
- `doc/api/openapi.draft.yaml`
- `doc/architecture/App_Tenant_Shared_Attachment_Model_v1.md`
- `doc/architecture/IAM_Token_Issuer_v1.md`
