clawdinators/docs/SECRETS.md
Josh Palmer 78f57df5a8 🤖 chore: move to moltbot upstream + sync tooling
What:
- swap nix-moltbot inputs, packages, and workflow automation
- update secrets wiring, repo seeds, and org references for moltbot
- fix self-update path and moltbot config/log defaults

Why:
- align infra with moltbot rename and new packaging
- restore update pipeline + consistent bootstrap artifacts

Tests:
- ./scripts/build-image.sh (fails: /build chmod permission on darwin)
2026-01-28 18:36:10 +01:00

3.4 KiB

Secrets Wiring

Principle: secrets never land in git. One secret per file, decrypted at runtime.

Infrastructure (OpenTofu):

  • AWS credentials via environment variable (required for infra/opentofu/aws).
  • Do NOT commit *.tfvars with secrets.

Image pipeline (CI):

  • AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_REGION / S3_BUCKET (required).
  • CLAWDINATOR_AGE_KEY (required; used to build the bootstrap bundle uploaded to S3).

Local storage:

  • Keep AWS keys encrypted in ../nix/nix-secrets for local runs if needed.
  • CI pulls credentials from GitHub Actions secrets (never from host files).

Runtime (CLAWDINATOR):

  • Discord bot token (required, per instance).
  • GitHub token (required): GitHub App installation token (preferred) or a read-only PAT.
  • Anthropic API key (required for Claude models).
  • OpenAI API key (required for OpenAI models).

Explicit token files (standard):

  • services.clawdinator.discordTokenFile
  • services.clawdinator.anthropicApiKeyFile
  • services.clawdinator.openaiApiKeyFile
  • services.clawdinator.githubPatFile (PAT path, if not using GitHub App; exports GITHUB_TOKEN + GH_TOKEN)

GitHub App (preferred):

  • Private key PEM decrypted to /run/agenix/moltinator-github-app.pem.
  • App ID + Installation ID in services.clawdinator.githubApp.*.
  • Timer mints short-lived tokens into /run/clawd/github-app.env with GITHUB_TOKEN + GH_TOKEN.

Agenix (local secrets repo):

  • Store encrypted files in ../nix/nix-secrets (relative to this repo).
  • Sync encrypted secrets to the host at /var/lib/clawd/nix-secrets.
  • Decrypt on host with agenix; point NixOS options at /run/agenix/*.
  • Image builds do not bake the agenix identity; the age key is injected at runtime via the bootstrap bundle.
  • Required files (minimum): moltinator-github-app.pem.age, moltinator-discord-token.age, moltinator-anthropic-api-key.age.
  • Also required for OpenAI: moltinator-openai-api-key-peter-2.age.
  • CI image pipeline (stored locally, not on hosts): moltinator-image-uploader-access-key-id.age, moltinator-image-uploader-secret-access-key.age, moltinator-image-bucket-name.age, moltinator-image-bucket-region.age.

Bootstrap bundle (runtime injection):

  • CI uploads secrets.tar.zst + repo-seeds.tar.zst to s3://${S3_BUCKET}/bootstrap/<instance>/.
  • secrets.tar.zst contains:
    • clawdinator.agekey
    • secrets/ directory with *.age files.
  • The host downloads + installs these on boot (clawdinator-bootstrap.service).

Example NixOS wiring (agenix):

{ inputs, ... }:
{
  imports = [ inputs.agenix.nixosModules.default ];

  age.secrets."moltinator-github-app.pem".file =
    "/var/lib/clawd/nix-secrets/moltinator-github-app.pem.age";
  age.secrets."moltinator-anthropic-api-key".file =
    "/var/lib/clawd/nix-secrets/moltinator-anthropic-api-key.age";
  age.secrets."moltinator-openai-api-key-peter-2".file =
    "/var/lib/clawd/nix-secrets/moltinator-openai-api-key-peter-2.age";
  age.secrets."moltinator-discord-token".file =
    "/var/lib/clawd/nix-secrets/moltinator-discord-token.age";

  services.clawdinator.githubApp.privateKeyFile =
    "/run/agenix/moltinator-github-app.pem";
  services.clawdinator.anthropicApiKeyFile =
    "/run/agenix/moltinator-anthropic-api-key";
  services.clawdinator.openaiApiKeyFile =
    "/run/agenix/moltinator-openai-api-key-peter-2";
  services.clawdinator.discordTokenFile =
    "/run/agenix/moltinator-discord-token";
}