Skip to main content
Version: 2.6

Configuration reference

Shine stores global runtime state under ~/.shine/ and creates ~/.shine/config.toml when global configuration is first needed. Configuration and manifest replacement does not delete the old file before renaming the staged file, including on Windows; a failed rename preserves the existing file.

On Unix, when SUDO_USER identifies the invoking user, Shine resolves that user's home through the system account database, including macOS Directory Services. Home-relative configuration and path expansion therefore use the invoking user's home rather than sudo's root HOME.

Interactive lifecycle Plan confirmation can authorize the listed external code for that operation only, without saving trust. Automation and --yes require an existing grant. Shell live always requires Development trust; explicit --run-generators inspection also requires a grant.

Common global fields​

presets_dir = "~/dotfiles/shine-presets"
# External shell presets default to snapshot; use live only for preset development.
external_shell_mode = "live"
presets_overlay_git = "https://example.com/team/shine-overlay.git"
presets_overlay_git_branch = "main"
app_default_dest_root = "~/.config"
sync_terminal_theme = true
gpg_recipients = ["user@example.com", "team-backup@example.com"]

secret_backend = "age"
age_recipients = ["age1tag1qexample...", "age1qteammate..."]
age_identity = "~/.shine/age/identity.txt"
age_identities = ["C:/Users/<user>/AppData/Local/age-plugin-phone/identity-....txt"]

[env]
HTTP_PROXY_PORT = "6152"
SOCKS5_PROXY_PORT = "6153"
PROXY_HOST = "127.0.0.1"
PROXY_NO_PROXY = "localhost,127.0.0.1,::1"
IMAGE_QUALITY = "80"
IMAGE_MAX_WIDTH = "1920"
IMAGE_MAX_HEIGHT = "1080"
MY_API_TOKEN = { value = "<token>", description = "Token for the internal API" }

[[env_proxy]]
command = "gh"
with = ["GH_TOKEN"]
# Defaults to true; false forwards without decrypting or injecting values.
enabled = false
FieldPurpose
presets_dirReplace built-in presets with a complete external preset directory
external_shell_modeExternal shell deployment mode; snapshot by default, optionally live
presets_overlay_gitGit overlay URL shallow-cloned and mirrored under ~/.shine/overlay/
presets_overlay_git_branchTracked overlay branch; omit for the remote default branch
app_default_dest_rootDefault root for legacy application presets without a destination
sync_terminal_themeEnable automatic terminal theme synchronization in managed Unix profiles; defaults on
gpg_recipientsDefault GPG recipients for shine env secret encrypt
secret_backendDefault secret backend; gpg when omitted
age_recipientsDefault encryption recipients for age
age_identityIdentity file for age: ciphertext; may default to ~/.shine/age/identity.txt
age_identitiesAdditional identity-file paths, merged after age_identity and deduplicated
[env]Values used by templates and shell helpers
[[env_proxy]]Transparent command rule: bare command, injected KEY or KEY=ALIAS list in with, and optional enabled defaulting to true

Legacy gpg_key_id and workspace [env.encryption].recipient accept only one recipient. Normal configuration reads never rewrite them. Preview and apply conversion to gpg_recipients with shine state migrate --dry-run and shine state migrate. The same migration converts legacy age1se... recipients to age 1.3 native age1tag... recipients; this removes the Secure Enclave plugin requirement from encrypting computers but makes targeting testable by someone who knows the recipient. env run and env secret seal prompt to migrate an old workspace when needed.

Phone recipients are not converted by state migrate. After upgrading and verifying tag support in the plugin and phone app, export with age-plugin-phone recipients -i <IDENTITY_STUB> --recipient-type tag, replace the corresponding age_recipients value, and reseal while retaining an independent recovery recipient.

When a command reads malformed trust.toml or ssh-secret-broker.toml, TOML parse errors identify the file without printing stored contents. A failed parse does not repair or rewrite it.

External-code trust​

External App hooks, generators, artifacts, Sys install scripts, executable profile content, and every external Shell command use target-scoped grants stored in the owner-only ~/.shine/trust.toml file. Do not edit this file or place grants in project configuration. Review and manage the current snapshot with:

shine trust inspect app/example
shine trust grant app/example
shine trust list
shine trust revoke app/example
shine trust inspect shell/tools/my-command
shine trust grant preset
shine trust grant app/example --development

A snapshot grant applies only to the reviewed target, capability, source layers, and complete effective category snapshot. An explicitly requested --development grant keeps trusting code edits from the displayed local source while the target, capability, and source directory/layer remain unchanged. This is long-term authorization of future source content, not continuing code review. Permission-table presence does not control code classification or grant eligibility. A grant does not replace the Plan shown for each operation. Retired allow_app_hooks and allow_sys_code fields are ignored and removed on the next configuration save. The preset target batches the current active snapshot for review or enrollment but persists separate grants for each target. New targets and capabilities require authorization; development grants additionally fail closed when their local source changes. Trust grants written by schema v1 remain listable and revocable but require review again.

Environment entry formats and descriptions​

[env] in global and project configuration, plus every shine.env.toml override, accepts a string or a detailed value:

[env]
PLAIN_VALUE = "example"
DETAILED_VALUE = { value = "example", description = "Example value used by build tasks" }
  • A string suits a value that needs no explanation.
  • Detailed value behaves like a string in env get, template replacement, secret encrypt/export, and env run --with.
  • description appears only in shine env list and is never passed to templates or children.
  • Inline descriptions override the same key in the preset <presets>/env.toml catalog.
  • shine env set preserves the description of an existing detailed entry.
  • Strings and detailed entries containing only value inherit lower-layer descriptions. An explicit description replaces the inherited description; an empty string clears it and disables catalog fallback. Value and description sources are tracked separately.
  • Invalid numbers, arrays, or tables without a string value report file and key instead of being ignored.

Project configuration​

Shine searches upward from the current directory for the nearest shine.config.toml. It is a sparse layer over global configuration: omitted fields inherit, and relative paths resolve from the file that declares them.

[[env_proxy]] follows the same layering. A project rule replaces a global rule with the same command; unrelated global rules remain. Prefer shine env proxy install, enable, and disable so configuration remains consistent with the shim under ~/.shine/bin/.

Shine 0.40.0 stopped recognizing project config.toml and .env.toml; rename them to shine.config.toml and shine.env.toml. Ordinary files with the old names are ignored.

It also stopped automatically migrating global ~/.shine/env.toml. Before upgrading, load it once with v0.39. After upgrading, move it to ~/.shine/shine.env.toml, manually merging an existing destination. Detection of the old file stops normal configuration loading with recovery instructions instead of silently dropping active values.

Directory and source precedence​

Relative --config-dir, SHINE_CONFIG_DIR, and SHINE_PRESETS paths are resolved against the invoking working directory before lifecycle planning or script execution. Child working-directory changes do not change the selected runtime roots.

SHINE_CONFIG_DIR has the highest priority. It changes the global configuration and runtime root and, outside a project, fixes the preset directory at $SHINE_CONFIG_DIR/presets/; SHINE_PRESETS and a global presets_dir do not override it.

When a project shine.config.toml is active, SHINE_CONFIG_DIR still selects the runtime root, but an explicit SHINE_PRESETS or project presets_dir can select the preset source. An inherited global presets_dir cannot override $SHINE_CONFIG_DIR/presets/.

Without SHINE_CONFIG_DIR, the base preset directory is selected in this order:

  1. SHINE_PRESETS;
  2. project shine.config.toml presets_dir;
  3. global config.toml presets_dir;
  4. default ~/.shine/presets/.

An external preset or overlay root may be a symbolic link. Shine resolves that root before capture and uses the resolved source for development trust; retargeting it requires review again. A broken root link is an error. Symbolic links inside the preset tree remain excluded from the snapshot. Special entries such as FIFOs, sockets, and devices fail snapshot capture with an error; Shine does not read them as preset files.

External shell categories default to snapshot: Shine copies them to ~/.shine/installed/shell/. On Unix, snapshots retain captured executable flags for scripts and helpers without copying ownership or privileged mode bits. Permission-only changes, including missing executable flags in an older installed snapshot, appear in shine update and are repaired by shine upgrade. After source edits, inspect with shine update and apply with shine upgrade. Set external_shell_mode = "live" only for development; source content takes effect on the next invocation, but changes to target, runtime, transforms, or env still require shine upgrade to rebuild managed entries. Live mode requires an explicit development trust grant; snapshot trust cannot authorize a launcher that reads mutable source. Existing live launchers are preserved, and status marks those missing development trust for review.

After upgrading Shine, run shine update and shine upgrade to refresh existing transformed Live Bun commands. Their refreshed entries use the installation's configuration directory for both rendering and declared environment values, even when the caller sets a different SHINE_CONFIG_DIR. Project configuration and shine.env.toml overrides still follow the caller's working directory. The refreshed PowerShell entries preserve empty arguments and literal --, including on Windows PowerShell 5.1. This change covers Live Bun commands with transforms, including commands without declared env; raw Live commands, native/source commands and snapshot commands keep their existing launch behavior.

An overlay replaces files at matching relative paths over the chosen base instead of replacing the whole tree. presets_overlay_dir and presets_overlay_git are mutually exclusive; use shine preset overlay link to configure them safely. presets_overlay_git and presets_overlay_git_branch are global-only keys in config.toml; run overlay link --git outside any Shine project. Projects can use presets_overlay_dir for a local overlay. A Git overlay becomes active after the link command clones it, or after a successful shine preset pull when configured manually. Later pulls forcibly mirror its checkout, so never edit ~/.shine/overlay/ directly.

Environment value precedence​

Later layers override earlier values:

  1. built-in defaults (runtime fallback only);
  2. active overlay shine.env.toml (shared baseline);
  3. global config.toml [env];
  4. global shine.env.toml;
  5. project shine.config.toml [env];
  6. project shine.env.toml.

Every installation uses this order. SHINE_CONFIG_DIR selects the global runtime root; paths are not fixed to ~/.shine/. Loading or saving unrelated settings never expands built-in defaults into files. Existing entries remain explicit, including historical entries equal to old defaults; delete an entry from its selected layer to inherit again. A lone shine.env.toml does not define a project. Only the nearest shine.config.toml defines the project root and its env file; ancestor projects are not merged.

Use shine env explain KEY for the value source, description source, override chain, and default write target. It hides all values unless --reveal is given and never decrypts. env get keeps its single-value output. See the environment guide for manual upgrade and rollback steps.

An override file has no [env] header:

HTTP_PROXY_PORT = "7890"
PROXY_HOST = { value = "127.0.0.1", description = "Local proxy host" }

Workspace environments​

shine.workspace.toml declares modes, ordered environment sources, and shared encryption recipients:

version = 2

[env]
modes = ["development", "production"]
default_mode = "development"
override_process_env = false
files = [
".env.shine.toml",
".env.local.shine.toml",
".env.{mode}.shine.toml",
".env.{mode}.local.shine.toml",
]

[env.encryption]
gpg_recipients = ["user@example.com", "team-backup@example.com"]
# Or use age:
# backend = "age"
# age_recipients = ["age1tag1qexample...", "age1qteammate..."]

Sources merge in list order. Current process variables win by default; env.override_process_env = true lets workspace values replace them.

For shine env secret seal and shine env run, encryption settings resolve from command-line arguments, then [env.encryption], then global ~/.shine/config.toml. Global configuration is useful for personal defaults; commit project-team GPG or age recipient lists here. Recipients are public-key information. Never commit private identity files or machine-local identity paths such as age_identity/age_identities.

Each source uses this structure:

version = 1

[plain]
PUBLIC_VALUE = "example"

[secret]
EXISTING_SECRET = true
PROMPT_ON_SEAL = false
PLAINTEXT_TO_SEAL = "<value to seal>"

[payload]
data = "<GPG ciphertext managed by Shine>"

shine env secret seal merges pending values into the encrypted payload and changes sealed entries to true. shine env run merges [plain] and decrypted values in source order. With usable recipients and only single-backend policy/sources, it also maintains an encrypted, mode-specific cache.

shine env run --with KEY[=ALIAS] injects a value from current Shine [env], preferring KEY_SECRET and then KEY. Explicit injection overrides workspace and process values.

Managed directories​

~/.shine/
├── config.toml
├── shine.env.toml
├── app-manifest.toml
├── shell-manifest.toml
├── proxy-manifest.toml
├── tasks.toml
├── bin/
├── http/
├── installed/
├── overlay/
├── rendered/
└── presets/
├── app/
├── shell/
└── sys/

Do not delete manifests manually and expect Shine to rediscover old installations. Prefer the corresponding uninstall --dry-run and uninstall commands.

Hybrid workspace encryption​

env.encryption.backend = "hybrid" requires both workspace gpg_recipients and age_recipients, each with a nonblank entry. GPG entries must be locally available full 40-hex primary public-key fingerprints. Global recipients are not merged. Merely configuring both lists does not enable hybrid.

Set hybrid_decrypt_backend = "gpg" or "age" in global config.toml. Local workspace operations may override this field in shine.config.local.toml next to the workspace; an absent file or field inherits the global preference. Shared shine.config.toml cannot override or save it. Ignore the personal file in Git; it accepts only this field and rejects malformed or unknown fields. The preference neither changes single-backend routing nor grants secret release. SSH broker does not accept remote personal configuration. Global secret_backend still accepts only gpg or age.

Hybrid compilation caches use the final selected backend and its workspace recipient list. Configuration changes re-evaluate cache validity without switching to another backend. See the environment guide.

Workspace file groups​

Workspace version 3 adds the file_groups.<name>.files array. Each entry requires ciphertext source and plaintext target. Groups must be nonempty; names contain only letters, digits, hyphens, and underscores. Paths support {mode} and resolve relative to the workspace directory. env.files may be omitted when groups are present. Version 2 does not accept file groups. Encryption reuses [env.encryption], without group-specific or environment-specific policies. See temporary secret files.