hammerkit

Secrets

A credential — an API token, a registry password, a service-account key — reaches a task or service through secrets. The build file only names where the value comes from; the value itself is read when the task or service starts, never written to the build file, the cache or hammerkit's logs.

tasks:
  publish:
    image: node:22-alpine
    secrets:
      - from: env:NPM_TOKEN          # host environment variable
        env: NPM_TOKEN               # → environment variable in the container
      - from: file:~/.config/gcloud/key.json
        path: /run/secrets/gcloud.json   # → read-only file in the container
    cmds:
      - npm publish

Each secret has a source and exactly one target:

FieldDescription
fromenv:NAME — a variable of the environment hammerkit runs in, or one given with --set; file:path — a host file, relative to the build file or ~/….
envInject the value as this environment variable.
pathMount the value as a read-only file at this path in the container.
cachetrue makes the value part of the task's cache key (see below). Default false.

Services take the same secrets. A service's init is a task and declares the secrets it needs itself.

services:
  postgres:
    image: postgres:17-alpine
    secrets:
      - from: env:DB_PASSWORD
        env: POSTGRES_PASSWORD
    healthcheck:
      cmd: pg_isready -U postgres

When the value is read

Planning a run never reads a secret, so ls, validate and runs of other tasks work without it. The value is read right before the container starts; when its source is missing the task fails before anything runs, naming the secret: secret NPM_TOKEN: environment variable NPM_TOKEN is not set.

Logs

Hammerkit masks every secret value it has read as *** in all it logs, including the output it streams from tasks and services. Each line of a multi-line value (a key file) is masked on its own. Values shorter than four characters are not masked — masking them would mangle unrelated output. A secret given as an env target is visible to anyone who can docker inspect the container, as with any environment variable; a path target is not.

Caching

By default a secret does not affect the cache: rotating a token does not rebuild everything that uses it. When a secret determines the output — a license key that changes what gets built, say — set cache: true. The cache key then holds a salted digest of the value, never the value itself, and a new value means a new key.

tasks:
  build:
    image: alpine:3.21
    secrets:
      - from: env:LICENSE_KEY
        env: LICENSE_KEY
        cache: true
    cmds:
      - ./build.sh

Runtimes

  • Docker: env targets are set on the container. A file: source is bind-mounted read-only. An env: source with a path target is written to an owner-only file in hammerkit's data directory, mounted read-only and removed when the container is. The file is readable by the host user only; an image whose process runs as another user (a database dropping to its own user, say) and needs to read it should get the secret as an env target, or from a file: source whose permissions you set.
  • Kubernetes: the values go into a Secret of the task or service, which the pod references (secretKeyRef for env targets, a read-only subPath mount for path targets). A task's Secret is deleted once it has run, a service's when it stops.
  • Local tasks take env targets only; a path target on a local task is an error.

A daemon service started with up keeps its secrets until down. A changed secret declaration recreates it on the next up; a rotated value does not — run hammerkit down first.

On this page