Skip to content

Secret-file separation + workspace lock file for safe team-shared git workspaces #151

@thomas-stegemann

Description

@thomas-stegemann

Why

Two related concerns for a git-backed workspace (#147) that aren't covered yet:

  1. Secrets in git: env vars carrying API keys / tokens / passwords belong nowhere near a commit. Bruno's .bru files cleanly separate vars.secret from vars so the secret section can be gitignored. Bowire today stores all env vars in one file — committing it means committing secrets.
  2. Concurrent writers: when the workbench is open and the operator runs bowire run --workspace-root . in another terminal, both want to write to the same disk store. Today that's a race; one writer wins, the other corrupts.

Both block real team adoption of the git-workspace shape.

Proposal — Part 1: Secret-File Separation

Storage shape

<workspaceRoot>/
  environments/
    staging.json              # non-secret vars committed
    staging.secrets.json      # secret vars — gitignored
    staging.example.json      # template — committed, no real values
  secrets/                    # workspace-wide secrets (cross-env)
    GH_TOKEN                  # one file per secret name
    DB_PASSWORD
  .gitignore                  # excludes *.secrets.json and secrets/

Resolution

  • Env-var resolution merges <env>.json + <env>.secrets.json at read time so the operator's {{API_KEY}} resolves whether the value lives in either file.
  • The secret.NAME source prefix (Multi-source {{...}} variable resolver #125 reserved this) resolves through secrets/NAME.
  • Editing a secret in the UI writes to the secret file; editing a non-secret writes to the regular file.
  • The Settings UI shows a clear visual distinction (key icon, masked value, "stored in secrets/" hint).

.example templates

  • For every staging.secrets.json a sibling staging.example.json lists the expected keys with placeholder values.
  • Committed to git so a new team member sees what they need to fill in.
  • Workbench auto-generates / updates the example when the operator adds a new secret name.

Migration

  • One-shot: walk every env, prompt the operator to mark which vars are secrets ("any var named *_KEY, *_TOKEN, *_PASSWORD, *_SECRET, Authorization defaults pre-flagged").
  • Splits the existing single-file env into base + secrets + example.

Proposal — Part 2: Workspace Lock File

Storage shape

<workspaceRoot>/
  .bowire-lock                # JSON: { pid, hostname, processName, openedAt }

Acquisition

  • Workbench writes .bowire-lock on workspace open, removes on workspace close + clean shutdown.
  • CLI commands that write to the storage root (bowire run, bowire mock --record, bowire workspace migrate-format) check for the lock + warn:
    Warning: workspace is open in another process (bowire workbench, PID 12345 on host bowire-dev).
    Continuing may overwrite unsaved changes.
    Override with --force-write.
    
  • Read-only commands (bowire run --dry-run, bowire export) skip the check.

Stale-lock detection

  • Lock includes PID + hostname. CLI tries kill -0 <pid> (Unix) / Process.GetProcessById (Windows) on the same host before refusing.
  • If the holding process is gone, CLI removes the stale lock + writes its own.

Crash recovery

  • Workbench on startup checks if its own old lock file exists; if so, treats it as "ungraceful shutdown" + offers to reload from the last known good state.

Acceptance

Secret-File separation

  • <env>.secrets.json + <env>.example.json files alongside the regular env file.
  • .gitignore defaults include *.secrets.json + secrets/.
  • Resolver merges both files at read time.
  • secret.NAME prefix resolves through secrets/NAME.
  • Settings UI shows key icon + masked value for secret-stored vars.
  • Migration prompt classifies existing vars; operator approves before splitting.
  • .example template auto-generated from current secret keys.

Workspace lock

  • .bowire-lock written + removed by the workbench.
  • CLI writes check the lock + warn with override flag.
  • Stale-lock detection via PID + hostname.
  • Crash-recovery prompt on next workbench start.
  • Lock file ignored by git (.gitignore default).

Composes with

Out of scope

  • HashiCorp Vault / 1Password / cloud KMS integration. Local-first; remote secret stores belong in a separate "secret provider plugin" track.
  • Per-secret access controls (RBAC on individual vars). Workspace-level membership is the unit.
  • Encrypted secrets at rest. Secrets stay plain on disk — the secret file is gitignored; the operator is expected to use OS-level disk encryption.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:workbenchUI / workbench surfaceroadmapTracked on the public Project board

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions