# JOURNAL — Phase 1c (Builder) Append-only. Each entry: what I did + verifying command/output + next. ## 2026-05-27 — Phase 1c kickoff / orientation **Context:** Phase 1 closed DONE (1c10fa5; all D1–D10 Adversary-PASS, no VETO; Adversary loop terminated for Phase 1). Starting Phase 1c per `plan-phase1c-full-reproducibility.md`. **Did:** - Read the phase plan in full + plan.md §6.1/§7/§9. Verified access & surveyed current state. - `ssh cc-ci 'hostname && nixos-version && systemctl is-system-running'` → `nixos` / `24.11.20250630.50ab793 (Vicuna)` / `running`. Healthy. - Surveyed secrets layout: - `secrets/secrets.yaml` holds 6 infra secrets: test_secret, drone_rpc_secret, drone_gitea_client_secret, bridge_drone_token, bridge_gitea_token, bridge_webhook_hmac. - `.sops.yaml`: recipients = `&host` (ssh-to-age of cc-ci ed25519 host key) + `&master` (off-box recovery, private at `/srv/cc-ci/.sops/master-age.txt`, sandbox-only). - `modules/secrets.nix`: `defaultSopsFile = ../secrets/secrets.yaml`, `age.sshKeyPaths = ["/etc/ssh/ssh_host_ed25519_key"]`. - Wildcard cert OUT-OF-BAND at `cc-ci:/var/lib/ci-certs/live/{fullchain.pem,privkey.pem}` (fullchain 2909 B, privkey 227 B → privkey looks like an EC key). `modules/proxy.nix` reconcile reads it from there and inserts as traefik `ssl_cert`/`ssl_key` swarm secrets; treats a missing cert as FATAL (operator precondition). - `flake.nix`: nixpkgs pinned `50ab793`, sops-nix pinned; single local `nixosConfigurations.cc-ci`. - Tooling note: sandbox host has NO sops/nix/age/ssh-to-age (`which` → only git). sops/age work must run on cc-ci (has nix + host age key) or via a sops binary fetched there with the master key. - Bootstrapped Phase-1c state: STATUS-1c.md, BACKLOG-1c.md, JOURNAL-1c.md (this file). REVIEW-1c.md left for the Adversary (its file per §6.1). Appended Phase-1c decisions to DECISIONS.md. **Decisions recorded (DECISIONS.md):** secrets linkage = **git submodule** (deviates from the flake-input default — rationale: no private-repo fetch credential needed at nix-eval on every rebuild, keeps `defaultSopsFile` a local path = minimal change + trivially byte-identical); bootstrap key for throwaway = **recovery age key via `sops.age.keyFile`**. **Next (W2):** create private `recipe-maintainers/cc-ci-secrets`; move secrets + wildcard cert into sops there as a submodule of the base; wire secrets.nix (cert→`/var/lib/ci-certs/live` via `path=`); prove byte-identical build + clean switch with TLS from the git cert. Then claim Gate W2. ## 2026-05-27 — W2 step 1: cc-ci-secrets repo created + populated (DONE) **Did:** - Created private `recipe-maintainers/cc-ci-secrets` via Gitea API (bot, org admin). HTTP 201, private=True. - Confirmed cc-ci host SSH key → age identity == `&host` recipient `age1h90utd…`: `ssh cc-ci 'nix shell nixpkgs#ssh-to-age --command ssh-to-age -i /etc/ssh/ssh_host_ed25519_key.pub'` → exact match. So I can decrypt/re-encrypt on cc-ci with the host key (master stays sandbox-only). - Built `secrets.yaml` on cc-ci (script with file redirections, no key material in argv): `sops -d` existing 6 secrets → append `wildcard_cert`/`wildcard_key` as YAML block scalars from `/var/lib/ci-certs/live/{fullchain.pem,privkey.pem}` → `sops -e`. Verified round-trip: - recipients: 2 (host+master) - keys: test_secret, drone_rpc_secret, drone_gitea_client_secret, bridge_drone_token, bridge_gitea_token, bridge_webhook_hmac, wildcard_cert, wildcard_key - cert sha256 file==decrypt `c1d96d61…`; key sha256 file==decrypt `9ec25d00…`; test_secret decrypts OK - Retrieved ciphertext (7219 B) to sandbox; created cc-ci-secrets repo (root `secrets.yaml`, own `.sops.yaml` w/ `path_regex: secrets\.yaml$`, README). Pushed to main (auth via per-command http.extraHeader; verified `.git/config` has NO creds). Remote lists .sops.yaml/README.md/secrets.yaml. - Cleaned `/root/cc-ci-secrets.yaml` + build script off cc-ci. **Layout decision:** cc-ci-secrets has `secrets.yaml` at ROOT → submodule mounts at base `secrets/` → base sees `secrets/secrets.yaml`, so `defaultSopsFile = ../secrets/secrets.yaml` is UNCHANGED. **Next (W2 step 2):** in base repo — replace tracked `secrets/` with the submodule; add `wildcard_cert`/`wildcard_key` sops secrets in secrets.nix (path= → /var/lib/ci-certs/live, + recovery keyFile); adjust proxy.nix framing; switch cc-ci to new config via `nixos-rebuild switch --flake 'git+file:///root/cc-ci?submodules=1#cc-ci'`; prove byte-identical + TLS-from-git-cert; then claim Gate W2. (Riskier — touches live server config; fresh iteration.)