ADR-006: Per-Project YAML Config with an Environment-Variable Secret
Design status: This page includes earlier planning assumptions. The current CLI contract and ADR-011 govern the CLI-first agent integration; MCP, SQLite preview handles, and server-side confirmation described below are not shipped.
- Status: Proposed
- Date: 2026-09-02
Context
F-008 is the foundational
dependency for every other feature — nothing reaches Jira without it. It requires
connection settings in a local file, the API token supplied via an environment variable
and never stored in the config file (FR-008-02), configuration validated before any
Jira-touching command runs (FR-008-03), and one configuration scoped to exactly one Jira
project (FR-008-04).
NFR-008-01 is absolute: the token must never appear in any file DevWorkWire writes.
The token is the highest-value asset in the system — it grants full user-level tracker access, and DevWorkWire holds no copy to revoke. Two open questions the requirements leave to design: what format the file takes, and where it lives.
Decision
- Format: YAML, holding only non-secret settings — Jira base URL and project key.
- Location: per-project,
.devworkwire.yamlin the working directory, beside the.devworkwire/state directory. - Token: process environment only. DevWorkWire does not discover or load
.envfiles. It is read at run time and lives only in process memory. - The token is never accepted as a CLI argument — command-line arguments are world-readable in the process table and land in shell history.
yaml.safe_loadonly.yaml.loadpermits arbitrary object construction and is forbidden, enforced by a lint rule rather than review habit.- Pydantic v2 validates the parsed structure at the boundary, with errors naming the offending field.
- Validation runs before any Jira call and checks connectivity, project access, and the
presence of the import-reference custom field — failing with
CONFIG_MISSING,CONFIG_INVALID,CREDENTIALS_MISSING, orREFERENCE_FIELD_MISSING. http://base URLs are rejected. TLS verification is always on, with no bypass option.- File permissions
0600;.devworkwire/must be gitignored. - Redaction is enforced by a logging filter, not call-site discipline, so the token and
Authorizationheader never appear at any verbosity.
Consequences
Positive
- The token never touches disk, so an accidentally committed config file exposes a base URL and project key — undesirable, but not a credential leak.
- One config = one Jira project matches
FR-008-04naturally, and an agent working inside a repo targets the right project with no extra setup. - The config travels with the repo, so teammates inherit the right settings.
- Refusing
.envdiscovery removes a real hazard: silently picking up a token from a parent directory the user did not intend. - Failing fast with a field-naming error beats surfacing a confusing
401several steps in. - Checking the reference custom field at this stage is important — creating it needs Jira admin rights, and without it dedup cannot work at all.
Negative
- Environment variables are visible to other processes running as the same user and can
leak through shell history. This is inherent to the mechanism and is the accepted
residual risk recorded in
F-008. Mitigation: documentation recommends a git-ignored.envsourced by the user's own tooling or a shell-profile export, and warns against shared machines. An OS keychain integration is the correct long-term answer and is deferred. - Rejecting
.envauto-loading costs setup convenience, and is felt most in the MCP case, where harnesses do not always pass shell environment through to spawned subprocesses. This may prove to be the wrong trade and should be revisited if MCP setup friction is a common complaint. - YAML brings a
PyYAMLdependency and its indentation and implicit-typing pitfalls, where TOML would have been stdlib on Python 3.11+. - Per-project placement means a config file per repo, and users must know to gitignore
.devworkwire/(though not the config itself). - One project per configuration means no multi-project workflows in the MVP.
Alternatives Considered
- TOML instead of YAML
- Considered because
tomllibis stdlib on the 3.11+ floor (ADR-002) — zero dependency, unambiguous typing, no indentation traps, and consistent withpyproject.toml. - Not selected: YAML was chosen for familiarity and consistency with existing tooling in
the workspace. This remains the strongest alternative if the
PyYAMLdependency or a YAML parsing bug becomes a problem.
- Considered because
- JSON
- Considered as stdlib, universal, and trivially machine-writable by an agent.
- Rejected: no comments, which hurts a hand-edited configuration file.
- Auto-load a
.envfrom the project directory- Considered for noticeably friendlier setup, especially under MCP harnesses.
- Rejected: adds a dependency and a file-precedence rule, and risks silently using a token from an unintended directory.
- Per-user config in the home directory
- Considered so setup happens once and works from anywhere.
- Rejected: binds the user to a single Jira project globally, awkward the moment they work across two repos.
- OS keychain for the token
- Considered as the strongest protection against the residual risk above.
- Deferred per
F-008scope: an interactive credential wizard is out of MVP scope.