DevOps Quickstart

One org, two secrets that never burn — just rotate. Store a Claude API key and a GitHub PAT as patchable org secrets, each locked to a dedicated principal so only the right service can read it.

Bootstrap the server

Start the server with --init. On first boot it creates a default org and two temporary principal keys — an admin and a reader. It prints them to stdout once.

Start with --init

export SIRR_MASTER_KEY="$(openssl rand -hex 32)"
sirrd serve --init

# Output (save these):
# Org ID:        org_a1b2c3d4
# Admin key:     sirr_pk_adminXXXX
# Reader key:    sirr_pk_readerXXXX

Export the values you need:

Export credentials

export SIRR_SERVER=http://localhost:39999
export SIRR_ORG=org_a1b2c3d4          # from --init output
export SIRR_ADMIN_KEY=sirr_pk_adminXXXX  # from --init output

Create two principals

Create a principal for each service that will access secrets. Principals are identities inside an org — each gets its own key with scoped permissions.

Create claude-agent principal

curl -X POST $SIRR_SERVER/orgs/$SIRR_ORG/principals \
  -H "Authorization: Bearer $SIRR_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "claude-agent", "role": "member"}'

# Save the principal ID:
export CLAUDE_PRINCIPAL_ID=princ_xxxxxxxx

Create github-ci principal

curl -X POST $SIRR_SERVER/orgs/$SIRR_ORG/principals \
  -H "Authorization: Bearer $SIRR_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "github-ci", "role": "member"}'

export GITHUB_PRINCIPAL_ID=princ_yyyyyyyy

Now create a key for each principal. Keys are the credentials used at runtime — the principal ID is internal, the key is what goes in .env or CI secrets.

Create a key for claude-agent

curl -X POST $SIRR_SERVER/orgs/$SIRR_ORG/principals/$CLAUDE_PRINCIPAL_ID/keys \
  -H "Authorization: Bearer $SIRR_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "claude-agent-key-1"}'

# Save the key (shown once):
export CLAUDE_KEY=sirr_pk_claude_XXXX

Create a key for github-ci

curl -X POST $SIRR_SERVER/orgs/$SIRR_ORG/principals/$GITHUB_PRINCIPAL_ID/keys \
  -H "Authorization: Bearer $SIRR_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "github-ci-key-1"}'

export GITHUB_KEY=sirr_pk_github_XXXX

Store a Claude API key

Push the Claude API key as a sealable org secret. Setting delete: false means the secret is sealed (not burned) when its read limit is reached — you can PATCH the value to rotate it without changing the key name.

The allowed_keys field locks this secret to the claude-agent principal's key ID so no other principal can read it.

Store ANTHROPIC_API_KEY as patchable secret

# First, get the claude-agent key ID (not the key value — the ID)
curl $SIRR_SERVER/orgs/$SIRR_ORG/principals/$CLAUDE_PRINCIPAL_ID/keys \
  -H "Authorization: Bearer $SIRR_ADMIN_KEY"

# Store the secret, locked to that key ID
curl -X POST $SIRR_SERVER/orgs/$SIRR_ORG/secrets \
  -H "Authorization: Bearer $SIRR_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "ANTHROPIC_API_KEY",
    "value": "sk-ant-XXXXXXXXXXXXXXXXXXXXXXXX",
    "delete": false,
    "allowed_keys": ["<claude-agent-key-id>"]
  }'

Verify claude-agent can read it:

claude-agent reads its secret

curl $SIRR_SERVER/orgs/$SIRR_ORG/secrets/ANTHROPIC_API_KEY \
  -H "Authorization: Bearer $CLAUDE_KEY"
# → { "key": "ANTHROPIC_API_KEY", "value": "sk-ant-XXXX..." }

And that github-ci cannot:

github-ci is blocked

curl $SIRR_SERVER/orgs/$SIRR_ORG/secrets/ANTHROPIC_API_KEY \
  -H "Authorization: Bearer $GITHUB_KEY"
# → 403 Forbidden

Store a GitHub PAT

Same pattern — sealable, locked to the github-ci principal's key.

Store GITHUB_PAT as patchable secret

# Get the github-ci key ID
curl $SIRR_SERVER/orgs/$SIRR_ORG/principals/$GITHUB_PRINCIPAL_ID/keys \
  -H "Authorization: Bearer $SIRR_ADMIN_KEY"

curl -X POST $SIRR_SERVER/orgs/$SIRR_ORG/secrets \
  -H "Authorization: Bearer $SIRR_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "GITHUB_PAT",
    "value": "ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "delete": false,
    "allowed_keys": ["<github-ci-key-id>"]
  }'

GitHub Actions can now fetch the token at runtime using the Sirr CLI or SDK. The PAT stays in the vault — it never touches your repo.

GitHub Actions step (example)

- name: Fetch GITHUB_PAT from Sirr
  env:
    SIRR_SERVER: ${{ secrets.SIRR_SERVER }}
    SIRR_TOKEN: ${{ secrets.GITHUB_SIRR_KEY }}
    SIRR_ORG: ${{ secrets.SIRR_ORG }}
  run: |
    export GITHUB_PAT=$(sirr get GITHUB_PAT --org $SIRR_ORG)
    # use $GITHUB_PAT in subsequent steps

Rotate a secret

When your Claude API key or GitHub PAT is regenerated, patch the value in place. The key name stays the same so consuming services need no config changes.

Rotate ANTHROPIC_API_KEY

curl -X PATCH $SIRR_SERVER/orgs/$SIRR_ORG/secrets/ANTHROPIC_API_KEY \
  -H "Authorization: Bearer $SIRR_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": "sk-ant-YYYYYYYYYYYYYYYYYYYYYYYY"}'
# → { "key": "ANTHROPIC_API_KEY", "updated": true }

The audit log records the patch operation. Run sirr audit to confirm:

Confirm in audit log

sirr audit --org $SIRR_ORG --action secret.updated

What is next

Was this page helpful?