arete-infisical

star 0

This skill should be used for any secrets work in an Areté (aretecp / Areté Intelligence) repo — when you need to read, set, rotate, or inject API keys / env secrets; wire a repo to Infisical; run an app with secrets injected; debug a "secret not found" / 404; set up GitHub Actions or a VPS systemd service to load secrets; or answer "how do we manage secrets here". Areté self-hosts Infisical at secrets.areteintelligence.ai with a folder-per-repo layout. Triggers include "infisical", "where are the secrets", "add an API key", "infisical run", "load secrets in CI", "machine identity", "rotate the key", and seeing a repo with a `.infisical.json` or an empty `.env` next to a real app.

sglyon By sglyon schedule Updated 6/3/2026

name: arete-infisical description: This skill should be used for any secrets work in an Areté (aretecp / Areté Intelligence) repo — when you need to read, set, rotate, or inject API keys / env secrets; wire a repo to Infisical; run an app with secrets injected; debug a "secret not found" / 404; set up GitHub Actions or a VPS systemd service to load secrets; or answer "how do we manage secrets here". Areté self-hosts Infisical at secrets.areteintelligence.ai with a folder-per-repo layout. Triggers include "infisical", "where are the secrets", "add an API key", "infisical run", "load secrets in CI", "machine identity", "rotate the key", and seeing a repo with a .infisical.json or an empty .env next to a real app.

Areté Infisical — Secrets Management

Areté manages secrets with a self-hosted Infisical instance. This skill captures the org's actual layout, the CLI workflow, the CI action, the VPS/systemd pattern, and the gotchas — so you can work with secrets correctly without re-deriving the topology every time.

The one-paragraph mental model

There is one Infisical instance, a small number of projects, and inside each project one folder per repo/app. A secret is addressed by (instance → project → folder path → environment) → KEY. Locally you authenticate as yourself (infisical login) and inject secrets into a process with infisical run -- <cmd>; in CI a machine identity does the same; the app code just reads process.env / System.get_env and never knows Infisical exists. Per-repo isolation is the folder path, not a separate project.

Topology (memorize this)

Thing Value
Instance (self-hosted) https://secrets.areteintelligence.ainot Infisical Cloud
Internal project slug arete-internal (UUID 989486e4-b880-40e0-97cd-6abf56abe6bc)
External project slug arete-external
Shared / org-wide project slug arete-shared
Environments dev, staging, prod (slugs, orthogonal to path)
Folder path /<repo-name> — e.g. /areteos, /arilearn-phx, /ari-panelist, /contact-intelligence, /main-website, …
GitHub org aretecp
  • Slug vs UUID: the CLI's .infisical.json and --projectId use the UUID; the GitHub Action and the Infisical UI use the slug. Slugs are stable, public identifiers — store them as Actions variables, never secrets.
  • Most app repos live in arete-internal under their own folder. arete-shared holds cross-cutting infra (Terraform, Entra, etc.) that multiple repos import.

CLI workflow (local dev — the common case)

One-time per machine / per repo

brew install infisical/get-cli/infisical     # macOS
infisical login                               # pick the self-hosted instance, your @aretecp.com / @aretepartners account
infisical init                                # in the repo root → writes .infisical.json (workspaceId only — safe to commit)

infisical login stores the instance + session under ~/.infisical/. infisical init writes a .infisical.json containing only the workspaceId — it does not pin the path or environment, so it is safe to commit and is not a secret.

Inject secrets into a process — the primary verb

# Run any command with the repo's secrets as env vars (env + path select the set):
infisical run --env=dev  --path=/<repo> -- npm run dev
infisical run --env=prod --path=/<repo> -- node server.js
infisical run --env=prod --path=/<repo> -- mix phx.server

infisical run injects the secrets only into that child process — nothing is written to disk. This is the canonical way to run an app: the source of truth stays in Infisical, there is no long-lived .env.

Read / write / delete

# List (CAUTION: prints VALUES). Prefer -o json piped to extract names when inspecting:
infisical secrets --env=prod --path=/<repo>
infisical secrets --env=prod --path=/<repo> -o json | jq -r '.secrets[].secretKey'   # names only

# Get one:
infisical secrets get ANTHROPIC_API_KEY --env=prod --path=/<repo>

# Set (Infisical overwrites if it exists — set again to update):
infisical secrets set "ANTHROPIC_API_KEY=sk-..." --env=prod --path=/<repo> --silent
infisical secrets set "A=1" "B=2" --env=prod --path=/<repo>     # multiple

# Delete:
infisical secrets delete OLD_KEY --env=prod --path=/<repo>

# Folders (discover the per-repo layout):
infisical secrets folders get --env=dev --path=/

When inspecting someone else's secrets, never echo values

Pull names only into a transcript / log. Use -o json | jq -r '.secrets[].secretKey', or check presence with booleans:

infisical run --env=dev --path=/<repo> --silent -- \
  node -e 'console.log({ANTHROPIC:!!process.env.ANTHROPIC_API_KEY})'

CI / GitHub Actions

Use the shared composite action aretecp/github-actions/actions/load-infisical-secrets@v1 — do not hand-roll the Infisical action. It defaults domain to the self-hosted instance and exports each secret to $GITHUB_ENV for later steps.

OIDC is the recommended auth method (no stored Infisical credentials — a short-lived GitHub OIDC token is exchanged at runtime):

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write            # ← required for OIDC
    steps:
      - uses: aretecp/github-actions/actions/load-infisical-secrets@v1
        with:
          method: oidc
          identity-id: ${{ vars.INFISICAL_OIDC_IDENTITY_ID }}   # non-secret UUID, store as a var
          project-slug: ${{ vars.INFISICAL_INTERNAL_PROJECT_SLUG }}   # = arete-internal
          environment: prod
          path: /<repo>
      - run: echo "secrets are in env now"

Universal Auth (legacy) passes client-id + client-secret from ${{ secrets.INFISICAL_CLIENT_ID/SECRET }} instead of method: oidc. It's being phased out in favor of OIDC.

Layering shared + project-specific (when a repo needs both arete-shared infra and its own folder): load shared first, project-specific second — the action writes to $GITHUB_ENV and last write wins, so the project value authoritatively overrides any shared-folder collision. Only use recursive: true on the shared load if you actually need its subfolders.

See aretecp/github-actions/actions/load-infisical-secrets/README.md for the full input table, JSON-output mode, and collision rules.

VPS / systemd (long-running prod service, non-interactive)

A systemctl service can't run infisical login interactively. Two correct patterns:

  1. Machine identity (Universal Auth) — create/grant a machine identity read access on the project, then have the unit inject at start:

    [Service]
    # client id/secret live in a root-owned env file, mode 600, NOT in the repo
    EnvironmentFile=/etc/ari/infisical.id
    ExecStart=/usr/bin/infisical run --projectId <UUID> --env=prod --path=/<repo> -- /path/to/node server.js
    

    With INFISICAL_CLIENT_ID / INFISICAL_CLIENT_SECRET (or INFISICAL_TOKEN) in that env file, infisical run authenticates non-interactively.

  2. .env fallback (interim/offline)EnvironmentFile=<app>/.env with a chmod 600 file. Acceptable as a backup or for an air-gapped on-stage box, but the source of truth is still Infisical: regenerate the file with

    infisical export --env=prod --path=/<repo> --format=dotenv > .env   # or: infisical run ... (preferred, no file)
    

Prefer (1) for anything networked; keep (2) only as a deliberate offline backup, and say so in the deploy doc.

Conventions / best practices

  • Source of truth is Infisical, not .env. A committed empty .env.example is documentation; a real .env is a local/offline cache at best. New keys go into Infisical (all relevant environments), not into a teammate's .env.
  • Folder path = repo name. When onboarding a repo, create /<repo> in arete-internal and infisical init the repo. Don't make a new project per repo.
  • Grant, don't multiply, identities. A new repo using CI gets the existing gh-actions-shared machine identity granted read access on its project — don't create a per-repo identity.
  • Slugs are public, values are not. Project slugs / identity UUIDs are fine as Actions variables or committed literals. Client secrets, tokens, and secret values never go in chat, logs, commits, or AI tools.
  • Rotate machine-identity client secrets quarterly (and immediately on suspected exposure): create the new secret, update the consumer, green a smoke run, then revoke the old one — revocation last so a failed rollout can revert.
  • Keep all envs in sync. ari-panelist, for example, holds the same key set across dev/staging/prod; diverging silently is a deploy footgun.

Troubleshooting

Symptom Cause / fix
404 Not Found / "requested entity is not found" on a path you know exists Almost always auth, not the path. Self-hosted Infisical returns 404 (not 401/403) for a workspace your session can't see. Run infisical login again, confirm the instance is secrets.areteintelligence.ai, then retry. Also check you init'd against the right project.
unknown flag: --format This CLI uses -o json (--output), not --format. Valid: yaml, json, dotenv.
Secrets missing inside the app You ran the app directly instead of via infisical run (or the systemd unit isn't wrapping it). infisical run injects only into its child process.
infisical run can't find config It walks up from CWD for .infisical.json. Run from inside the repo, or pass --projectId <UUID>.
CI: a shared key shadows the app's value Load order. Load arete-shared first, the project folder second — last write wins. Drop recursive on shared if you don't need subfolders.
"Secret not found" but it's in the UI Wrong --env or --path. Verify: infisical secrets --env=<env> --path=/<repo> -o json | jq -r '.secrets[].secretKey'.

Canonical sources in the monorepo set

  • aretecp/github-actions/actions/load-infisical-secrets/ — the CI action + its README (input table, OIDC vs universal, layering).
  • aretecp/github-actions/docs/runbooks/infisical-machine-identity.md — identity setup, org-secret wiring, rotation, compromise response.
  • areteos/.claude/skills/infisical/SKILL.md, areteos/bin/deploy, areteos/bin/setup-secrets — the original repo-level pattern (Docker Compose + infisical run wrapper, idempotent setup).
Install via CLI
npx skills add https://github.com/sglyon/sglyon-claude-plugins --skill arete-infisical
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator