# Secret Handling Strategy

Pre-v1 design: how the Shellops DSL and runtime treat secrets so they cannot leak through the pipeline surface, while ops remain the place where secrets are actually used.

## Goals

- Secrets never escape the intended boundary: pipelines **route** secrets; ops (bash `.op.sh` files) **consume** them.
- Compile-time rules make accidental leakage at the DSL level structurally impossible, without whole-program taint analysis.
- Runtime output masks secrets by default; developers can opt into verbose disclosure only locally.

## Compile-time restrictions

`@secret` values are allowed in exactly two places:

1. **Named arguments to ops** — e.g. `op(token: $SECRET)`.
2. **Logging functions** — references are accepted; the runtime masks them in output.

Any other use is a **compile error**.

### Illegal operations on secrets

The compiler rejects:

- Concatenation with non-secret values or other secrets in the pipeline language.
- Template interpolation that would embed a secret into a string visible to the pipeline.
- Assignment to variables that are not typed as secrets.
- Use inside arbitrary expressions (arithmetic, conditionals beyond what the grammar allows for routing, etc.).
- Use inside **inline bash blocks** in `.ops` source.

Secret **manipulation** (building headers, connection strings, signed payloads) belongs in **ops**, not in pipelines. For example, constructing `Authorization: Bearer …` is `http.api.sh` (or similar op) territory, not something expressed in the pipeline DSL.

### Why no taint tracking

The allowed set of positions for `@secret` is intentionally tiny. If the only legal flows are “into op args” and “into logging (masked),” leakage through the DSL is ruled out by construction. A separate taint-tracking pass is unnecessary for that guarantee at the language level.

## Runtime masking

- **Fixed-length masking:** secrets render as a constant mask (e.g. `*****`), not a mask whose length matches the secret. Variable-length masking leaks the secret’s length.
- **Secret names:** hidden by default in non-local contexts (e.g. CI logs, shared artifacts).
- **Errors:** secret values are stripped from error context; placeholders such as `<secret:VAR_NAME>` identify which binding failed without echoing the value.

## Development flags

Both are **off** by default.

| Flag | Behavior |
|------|----------|
| `--show-secrets` | Emit actual secret values in output. **Local development only**; must not be relied on in CI. |
| `--show-secret-names` | Emit secret variable names in output. **Local development only.** |

The engine should treat these as explicit unsafe modes suitable for a trusted machine, not as a general logging mode.

## Environment declaration

- `@secret TOKEN` declares a secret environment variable (or binding) for the pipeline.
- **Hardcoded defaults for secrets are forbidden** — a compile error if the source tries to supply a literal default.
- Values come only from the **environment** or **CLI flags**, never from checked-in source.

## Op-level responsibility

- Ops receive secrets as **named arguments** and use them inside trusted bash (or other op implementations).
- Ops are **trusted code**: they may construct headers, connection strings, temporary files, etc.
- **Boundary summary:** pipelines declare and pass secrets; ops perform all string assembly and I/O that touches raw secret material.

## Open points (pre-v1)

- Exact spelling of compile diagnostics for each illegal secret use.
- Interaction between `--show-secrets` and structured (`--json`) output (likely: still redact unless flag set, or forbid combination in CI profiles).
