mise-cache is the official self-hostable remote cache service for mise tasks. It implements version 1 of the mise content-addressed cache protocol with immutable blobs, atomic action-result commits, namespace isolation, and streaming transfers.
- BLAKE3 and SHA-256 content-addressed storage
- Filesystem storage for a single-node installation
- S3-compatible storage for production, including MinIO
- In-memory metadata for development or PostgreSQL for durable, horizontally scalable deployments
- Static and OIDC bearer authorization with per-namespace read/write grants
- Recursive output-tree validation before an action result becomes visible
- Batch missing-blob queries and Prometheus metrics
- Docker Compose and Helm deployments
The development stack starts the service, PostgreSQL, and MinIO:
docker compose up --buildIt listens on http://localhost:8080. The included token is development-token and permits the default namespace. Change it before exposing the service.
For a local filesystem-backed instance:
cargo run -- \
--allow-anonymous \
--data-dir ./data \
--listen 127.0.0.1:8080Anonymous access is intended only for a trusted local network. Production installations should terminate TLS at an ingress or proxy and configure tokens.
Every option has a matching environment variable and CLI flag. Run mise-cache --help for the complete list.
| Environment variable | Default | Purpose |
|---|---|---|
MISE_CACHE_LISTEN |
0.0.0.0:8080 |
Listen address |
MISE_CACHE_STORAGE |
filesystem |
filesystem or s3 |
MISE_CACHE_DATA_DIR |
/var/lib/mise-cache |
Filesystem blob root |
MISE_CACHE_DATABASE_URL |
memory:// |
PostgreSQL URL or development memory store |
MISE_CACHE_S3_BUCKET |
— | Required for S3 storage |
MISE_CACHE_S3_PREFIX |
v1 |
Object-key prefix |
MISE_CACHE_S3_ENDPOINT |
AWS default | S3-compatible endpoint |
MISE_CACHE_S3_REGION |
us-east-1 |
S3 region |
MISE_CACHE_S3_PATH_STYLE |
false |
Enable for MinIO and similar services |
MISE_CACHE_TOKENS_JSON |
— | Token grants, described below |
MISE_CACHE_OIDC_PROVIDERS_JSON |
— | Trusted OIDC providers and claim grants, described below |
MISE_CACHE_ALLOW_ANONYMOUS |
false |
Allow access without configured grants |
MISE_CACHE_MAX_BLOB_BYTES |
5368709120 |
Maximum upload size |
AWS credentials use the standard AWS SDK credential chain, including environment variables, workload identity, ECS, and EC2 roles.
MISE_CACHE_TOKENS_JSON is an array of grants. Namespace patterns may be an exact name, *, or a prefix ending in /*.
[
{
"token": "replace-with-a-secret",
"read": ["acme/*", "public"],
"write": ["acme/project-a"]
}
]Rotate tokens by deploying the old and new grants together, moving clients to the new token, and then removing the old grant. Do not put the JSON directly in a Helm values file; inject it through a Kubernetes Secret.
OIDC lets CI systems use short-lived identity tokens instead of stored cache secrets. Configure trusted issuers, acceptable audiences, and one or more claim-based grants with MISE_CACHE_OIDC_PROVIDERS_JSON:
[
{
"issuer": "https://token.actions.githubusercontent.com",
"audiences": ["https://cache.example.com"],
"rules": [
{
"claims": {
"repository": "jdx/mise",
"repository_owner_id": "216188"
},
"read": ["jdx/mise"],
"write": ["jdx/mise"]
}
]
}
]The server discovers the issuer's JWKS endpoint, verifies the signature, issuer, audience, expiry, not-before time, and subject, then applies the first matching authorization rule. Rules are alternatives; every claim within a rule must match exactly. A configured claim may be an array of accepted scalar values, and a token claim may itself be an array. Namespace grants use the same exact, *, and prefix/* forms as static tokens.
Authorization is deny-by-default: every provider needs at least one audience and rule, and every rule must constrain at least one claim. Pin stable identity claims such as GitHub's numeric repository_owner_id alongside the repository name; add ref, environment, or other claims when only a narrower workflow identity should be able to write. Symmetric JWT algorithms are never accepted.
Optional provider settings are:
| Field | Default | Purpose |
|---|---|---|
discovery_uri |
<issuer>/.well-known/openid-configuration |
Override OIDC discovery |
jwks_uri |
discovered | Use an explicit JWKS endpoint and skip discovery |
algorithms |
supported asymmetric algorithms | Restrict accepted JWT signing algorithms |
jwks_refresh_seconds |
300 |
Refresh interval; an unknown key ID also requests a refresh |
clock_skew_seconds |
60 |
Leeway for expiry and not-before validation |
The service makes three bounded attempts to fetch provider metadata and keys at startup. It refreshes stale keys during token validation, and an unknown key ID requests a JWKS refresh. Refresh attempts have a 30-second cooldown to prevent attacker-controlled key IDs from amplifying outbound requests. If a periodic refresh temporarily fails, an already-cached key remains usable; an unknown key never does. Use HTTPS for discovery and JWKS endpoints outside a trusted private network.
Until mise can acquire the job identity token itself, a workflow can request it from GitHub and pass it through the existing cache-token environment variable:
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- name: acquire cache identity
id: cache-identity
env:
OIDC_AUDIENCE: https://cache.example.com
run: |
response="$(curl --fail --silent --show-error \
-H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=$OIDC_AUDIENCE")"
token="$(jq -r .value <<<"$response")"
echo "::add-mask::$token"
echo "token=$token" >> "$GITHUB_OUTPUT"
- run: mise run test
env:
MISE_TASK_CACHE_REMOTE_TOKEN: ${{ steps.cache-identity.outputs.token }}Treat the output as a secret even though it is short-lived. Set the audience in the workflow to exactly one of the provider's configured audiences.
All cache requests send Mise-Cache-Namespace and, unless anonymous access is enabled, Authorization: Bearer ….
GET /v1/statusGET /v1/capabilitiesGET|PUT /v1/blobs/{algorithm}/{hash}/{size}POST /v1/blobs:missingGET|PUT /v1/action-results/{algorithm}/{hash}/{size}GET /metrics
Blobs and action results are immutable. Repeating an identical write is idempotent; attempting to replace an existing action result returns 409 Conflict. The server verifies uploaded content and all referenced blobs before publishing an action result.
Run multiple stateless replicas against the same PostgreSQL database and S3 bucket. Readiness and liveness probes use /v1/status. Scrape /metrics with Prometheus. S3 lifecycle policies control retention; PostgreSQL records are deliberately small and may be retained or expired by an operator-supplied job according to organizational policy.
Back up PostgreSQL and enable S3 versioning or replication as required. The service never exposes a deletion endpoint, so retention and disaster recovery remain administrative concerns.
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-featuresThe protocol specification and mise client live in jdx/mise.
MIT