Skip to main content
preloop init, preloop setup github, and preloop secret write one configuration file, ~/.preloop/config.toml. The engine reads the same file when it starts. The commands are the supported way to change credentials and secrets. You can also edit the file, and the reference below lists its keys.

Where files live

PRELOOP_HOME defaults to ~/.preloop. Set it to move the whole state directory. Set credential_store = "os" to use the macOS Keychain, Windows Credential Manager, or Linux Secret Service for engine and GitHub credentials. The file store remains the fallback when the OS store is unavailable. Every call into the OS credential store is bounded. A keychain or Secret Service that does not answer within 10 seconds (it is waiting on an unlock prompt nobody will see, or its daemon is hung) fails that call with an error saying so, and preloop treats the OS store as unavailable for the rest of the process instead of waiting again. Outbound TLS never asks the OS at handshake time: preloop trusts the bundled Mozilla roots, the certificates in the OS store (read once at startup, for at most 5 seconds), and PRELOOP_GITHUB_CA_FILE / SSL_CERT_FILE. A CA added to the OS store while the engine runs is picked up on the next restart. On a headless macOS host, keep the login keychain from re-locking itself, or the next program that reads it raises an unlock dialog on the unattended console and securityd blocks every other keychain client behind it:
With no flags this removes the idle timeout and the lock-on-sleep setting. PRELOOP_CONFIG moves only the configuration file. The credential directory then sits next to that file.

Configuration file

The file is plain TOML. Keys that the CLI writes are marked in the comments:
Keys under [github] that hold credentials, such as app_pem_ref and webhook_secret_ref, point at entries in the credential directory. Leave them to preloop setup github.

Other tables

GitHub credentials

Use a GitHub App, which mints a short-lived token for each job. A fine-grained personal access token is the fallback for organizations that block App installs.
The App flow stores the private key and webhook secret in the credential directory, and the file keeps only references. GitHub App covers the setup and webhook options. GitHub tokens explains what each job’s token can do. mint_failure decides what a job gets when the App cannot mint a token. local (the default) leaves the job without an App installation token. error rejects the run. pat falls back to the stored personal access token, which is broader, so choose it deliberately.

Secrets

Secrets work like GitHub’s three tiers of Actions secrets. Each name is resolved for a job from the most specific tier first: Manage them with:
  • Names are uppercase letters, digits, and underscores.
  • Environment names use letters, digits, hyphens, and underscores, up to 255 characters, and cannot start with - or _.
  • If the engine is running, set and rm apply to the next run immediately. If it is not, the CLI writes the config file, and the engine reads it on its next start.
  • With secrets_store = "memory" or PRELOOP_SECRETS_STORE=memory, values live only in the running engine, and set needs a running engine.
Workflows read secrets the usual way:
preloop run checks the workflow for secrets.* names the engine has no value for, and prints the preloop secret set commands to fix them. Add --strict-secrets to make a missing secret fail the run. A secret with no value reaches the step as an empty string. Runs from untrusted sources get no stored secrets. This includes pull requests from forks that arrive through the GitHub webhook. Runs you submit with preloop run are trusted.

How stored values are protected

Values in the file are encrypted. The file holds sealed:v1: ciphertext, and the data key lives in the credential store. A 0600 secrets.key file next to the config is written only while that store cannot hold the key, and it is migrated into the store once it can. preloop secret list and preloop doctor never print values, and job logs mask them. If the key cannot be read, for example while the macOS login keychain is locked, startup fails and names what to unlock. Preloop never generates a replacement key over sealed secrets, because a new key cannot open them. A secrets.key file that disagrees with the store is renamed secrets.key.orphan-<unix-ts>, never deleted. To keep secrets out of the configuration file entirely, use secrets_store = "memory" with a systemd encrypted credential. Deploy describes the credential setup.

Durable state

The engine keeps run history, queued jobs, runners, sessions, and logs. They survive restarts.
  • SQLite is the default. The database is $PRELOOP_HOME/state/preloop.db, and the engine needs no setup.
  • Postgres is optional. Set PRELOOP_STORE_URL or pass --store postgres://… to preloop serve, after running preloop store migrate.
A fresh local SQLite store is prepared by the supported first-run paths (preloop serve, preloop init, preloop server install). A serving process never initializes or upgrades a store that already exists; use preloop store migrate for existing or mismatched schemas, and provision a Postgres store yourself. Storage covers backups, retention, and moving from an older database.

Limitations

  • The native API uses one shared system credential, PRELOOP_SYSTEM_TOKEN. The engine has no per-user identities, so anyone holding that token has full access to the native API. Give each operator their own engine.
  • preloop push without a run ID publishes the most recent run on the engine, which may belong to someone else.
  • Per-user identities and roles are on the roadmap.