Drop CE S3 and KMS options
Epic: Security audit of development keys and container artifacts. This child = Architecture C5 (PRD R4 AC4.3). It also creates the `[feature.aws]` row that the phase-2 aws cut extends.
## Original requirement (verbatim, epic body)
- "2. The mirror build should prune the `aws/`, Slack, Telegram and headscale code it does not need. That reduces both the copying surface and what attackers learn."
- "It should not break anything in prod or dev"
Operator decisions: D2 cut all of `aws/` (refactor the CE paths that use it). D5 drop both the CE S3 and KMS options and refuse at boot with a message naming the setting; release notes call it out.
## Approach (Architecture C5)
- `apps/api/app/attachment_storage.py:102-114` `target()`: on CE, always return `local_volume()`.
- Gate the S3 branch and its top-level `presign`/`AwsCredentials` imports (:38-39, :128) under `edition_features.enabled("aws")`.
- Do the same in `session_transcript_archive.py:31,334`, `session_events.py:29`, `routes/agent_sessions.py:54` and `routes/sessions.py:59`.
- The volume readers (`session_transcript_archive.on_volume` :342, `_read_volume_manifest` :228-232) become the only CE path.
- `apps/api/app/credential_keys.py`: the `KmsError` import (:54) and `KmsKeyProvider` (:193) go under `enabled("aws")`. Same for `routes/public.py:122`.
- Add `[feature.aws]` to `packages/editions/features.toml` with `label = "Amazon Web Services"`, `editions = ["cloud"]` and no `paths` yet. `edition_features.enabled` raises `KeyError` on an unknown slug (`apps/api/app/edition_features.py:269-273`), so the row lands with the first guard.
- CE-only boot refusal, single home `ce_bootstrap.refuse_removed_settings()`, called beside `ensure_secrets()`:
- It covers the attachment bucket env var (confirm its name at `attachment_storage.py:102-114`), the transcript bucket setting if separate, `CREDENTIAL_KMS_KEY_ID` and `CREDENTIAL_KMS_PREVIOUS_KEY_IDS`.
- If any is set → `RuntimeError` naming the variable and saying it was removed from the community edition and must be unset. Never the value.
- Upgrade note in `infra/community-edition/README.md` (upgrade section) and `RELEASE-CHECKLIST.md`:
- Before upgrading, copy the bucket into the volume's `attachments/`, then unset the setting.
- An install on KMS cannot unwrap its secrets after the upgrade: re-enter credentials, or stay on the previous release.
- No migration code: the bucket is the operator's.
- Every guard must keep the cloud behaving exactly as today: `aws` is enabled on cloud.
## PRD R4 — this child's ACs, verbatim
- AC4.3 CE drops the operator S3 bucket and KMS ARN options: CE stores attachments/transcripts on its volume and wraps under its generated keypair only. A CE install that still sets a bucket or `CREDENTIAL_KMS_KEY_ID` refuses to start with a message naming the setting to unset (never a value).
- AC4.5 The cloud edition loses nothing: Slack, Telegram, tailnet, cloud nodes, S3, KMS, Bedrock still work on dev.
- AC4.2 No shipped file imports a pruned module (the CE import-closure check / CE tests pass).
Credentials & reach:
- Quick/full gate, pytest, CE tree build + `check_tree`, installer harness, tree console build | Worktree only (`./dc`, `tools/community_edition/*.py` present) | verified — files present on h2; no credential needed
- PR checks / merge lane / Jaah Deploy run status | `jaah-forge` (pre-authenticated for the task) | verified — read verb `pr list` succeeded from h2
- Dev console boots, sign-in, Slack/Telegram settings, attachment upload (AC2.3, AC4.5, AC9.2) | `org/console/dev-login` (credential_get succeeded); environment: dev control plane | **deferred** to a node with reach. dev timed out from h2 (prod `app.jaah.ai` answered 200). Operator answer A5: a green Jaah Deploy is the gate, and the browser check runs on a node that reaches dev; a builder without reach records it deferred
Out of scope: PRD §5, verbatim:
- Do NOT change `infra/community-edition/node/install_clis.sh`'s vendor `curl https://claude.ai/install.sh | bash` (D8; deferred follow-up).
- Do NOT sign or digest-pin the cloud deploy images pushed by Jaah Deploy, nor add deploy-time verification (D3 chose CE + base images only).
- Do NOT add a CI/terraform guard over ECS env (D7 verify-only).
- Do NOT cosign-sign `SHA256SUMS` (D4).
- Do NOT remove Slack, Telegram, headscale, AWS, KMS, S3 or Bedrock from the cloud edition — the prune is CE-tree only.
- Do NOT change the session-secret / dev-seed / insecure-agent refusals' semantics or the `FIXTURE_APP_ENVS` set.
- Do NOT touch `infra/jaah/terraform` resources (only read them for R2).
- Do NOT change port binds, setup-loopback rule, cookie flags or rate limits (A6 positives).
- Architecture boundary: do NOT add `paths` to `[feature.aws]` (the phase-2 aws-cut child does); no migration code for operator buckets; do not touch slack/telegram/headscale importers (sibling child).
Done means:
- AC4.3 — pytest on CE with each removed setting (attachment bucket, transcript bucket if separate, `CREDENTIAL_KMS_KEY_ID`, `CREDENTIAL_KMS_PREVIOUS_KEY_IDS`) → RuntimeError naming it and not containing the value; CE with none → volume target; cloud with a bucket set still → S3 target (pytest). CE release notes (README upgrade section + RELEASE-CHECKLIST upgrade note) state the removal with the copy-to-volume and re-enter-credentials steps — a refusal with no operator guidance fails.
- AC4.2 — `check_tree.py` green; the mirror engine's strict check for `jaah-ce` green; `pytest tools/tests/test_community_edition.py` green; the built tree's api migrates and answers `/api/health`.
- AC4.5 — full gate green; on dev a task attachment uploads and downloads (browser part deferred — dev unreachable from h2; operator A5: green Jaah Deploy is the gate).
- AC9.1 `./dc check quick` green on the branch; merge lane `./dc check full` green.
- AC9.2 The PR's Jaah Deploy succeeds to dev; dev console loads and a user can sign in.
Activity
- Draft
- Ready
- Runner started