J1: Repo Onboarding & Shared / Per-User BYOK Routing (~5 Minutes)
A collaborator signs into the Control Panel via GitHub OAuth, installs the atobar-flow-agent GitHub App on the client
repository, and runs onboard.sh (J1.1). API keys (gemini, claude,
openai, deepseek, grok) are envelope-encrypted with AWS KMS (AES-GCM)
using (tenant_id, provider) AAD in control-panel/src/control_panel/llm_keys.py and routed per repository and
per role in tenant_role_llm (J1.2, [AS-BUILT]).
The onboarder Spot role generates the initial SotaWiki (including a validated Mermaid
architecture diagram, J50) and opens a PR adding .github/workflows/flow-trigger.yml and
flow-metrics.yml (J1.4, [AS-BUILT]). On the Vision roadmap (J1.3, J53),
keys transition to per-invoking-user repository keys (tenant_id, user_login, provider) and
profile-level Default Keys (user_llm_keys, GET /profile), while EC2 Spot provisioning is gated by a per-user feature flag (platform_users.ec2_enabled, defaulting to false with a thin dim-grey ✕-closable top ribbon listing the admin user(s) to ping, toggled by a Superuser in GET /admin/users) and a 2-item trigger hold comment (<!-- atobar:byok-hold:{user_login} -->).
# 1. Connect any client repository clone (~5 minutes):
$ curl -fsSL https://raw.githubusercontent.com/atobar-flow/atobar-flow/main/scripts/onboard.sh | bash
# 2. AWS KMS Envelope Encryption in control-panel/src/control_panel/llm_keys.py:
# [AS-BUILT]: AAD binds (tenant_id, provider) in public.tenant_llm_keys
aad = f"{tenant_id}:{provider}".encode("utf-8")
ciphertext = aesgcm.encrypt(nonce, plaintext_api_key.encode("utf-8"), aad)
# [VISION / J53]: Per-invoking-user repo keys & profile-level Default Keys (public.user_llm_keys):
# - Repo key PK: (tenant_id, user_login, provider)
# - Profile key PK: (user_login, provider) with unique partial index where is_default = true
# - Unonboarded trigger gate: posts <!-- atobar:byok-hold:{user_login} --> before Spot launch
tenant_tokens(SHA-256 hashedatb_...token)tenant_llm_keys(KMSAES-GCMciphertext + AAD)tenant_role_llm(per-role provider, model, effort)security_audit_log(append-only key mutations)[VISION]user_llm_keys(profile Default Keys)
Enforced by tests/test_table_ownership.py: only control-panel can read or write
encrypted key tables. Plaintext keys never enter flow-engine; Spot VMs claim them once via
POST /v1/runs/{run_id}/llm-key.