Pipeline identity
Per-run tokens, secret injection, and why pipelines never need long-lived credentials.
Workload tokens
Every pipeline run gets a short-lived, Ed25519-signed JWT minted specifically for that run. The token identifies which org, team, and repo triggered the run, what the token is allowed to do, and when it expires (minutes, not months).
The token's sub claim uses the repo's UUID —
not its name. If a repo is deleted and a new one is created with the same name,
it gets a new UUID and a new identity. No name-recycling attacks.
Token scopes
| Scope | Grants |
|---|---|
| pkg:read:{org} | Pull npm packages from the org's registry |
| pkg:write:{org} | Publish npm packages to the org's registry |
| img:read:{org} | Pull container images |
| img:write:{org}/{image} | Push a specific container image |
Secrets
Secrets are defined at three levels. Narrower scopes override broader ones — a repo secret with the same name as an org secret wins.
| Scope | Visible to |
|---|---|
| Org | All pipelines in the org |
| Team | That team's pipelines only |
| Repo | That repo's pipelines only |
Secret names must be uppercase with underscores. They are injected as environment
variables in pipeline steps using the ${{ secrets.NAME }} syntax.
Values are encrypted at rest, never returned by the API, and scrubbed from logs
including base64 and hex encodings.
Ephemeral signing keys
For provenance attestation, each pipeline run also gets an ephemeral Ed25519 key pair. The private key exists only for the duration of the run. The public key is embedded in a certificate signed by the platform — verifiable without trusting the runner.
Pipeline sandbox
Every pipeline step runs inside an isolated container with hard enforcement at the runtime level. This applies to every image — gittan base images, custom images, external images. The image controls what tools are available; the sandbox controls what those tools can do.
| Enforcement | What it does | What it prevents |
|---|---|---|
| Read-only root filesystem | Image filesystem is immutable. Only /workspace and /tmp are writable. | Installing packages at runtime (apt-get, apk add) |
| Network isolation | Install steps can reach package registries. Build/test steps have no outbound network. | Scanning internal networks, data exfiltration, calling external services |
| Dropped capabilities | No CAP_NET_RAW, CAP_SYS_ADMIN, or other Linux capabilities. | Container escape, privilege escalation, raw socket access |
| Resource limits | CPU, memory, and wall-clock time are bounded per step. | Crypto mining, resource exhaustion, runaway processes |
| Step isolation | Each step is a fresh container. No persistent state between steps except /workspace. | Secrets leaking between unrelated steps |
| Tenant isolation | Pipelines from different orgs share nothing. No cross-tenant filesystem, network, or process visibility. | One customer affecting another |
The sandbox protects gittan's infrastructure and other customers. It does not protect an org from its own org owner — the org owner is the admin and can run whatever they want within their sandbox. This is the same trust boundary as any cloud provider: your admin can delete your own resources, but can never reach another customer.