Skip to content

harness exec#73

Draft
shwetamurali wants to merge 4 commits into
mainfrom
shweta/harness_exec
Draft

harness exec#73
shwetamurali wants to merge 4 commits into
mainfrom
shweta/harness_exec

Conversation

@shwetamurali

@shwetamurali shwetamurali commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Description

Adds tfctl harness exec [--allow-delete=CLASSES] -- <command> which is a human-authorized, session-scoped, auto-reverting permission that lets nested tfctl invocations perform noninteractive HCP Terraform DELETEs (e.g. for a coding agent or CI script).

By default, tfctl api -X DELETE requires an interactive confirmation. harness exec lets a human deliberately opt in, per session, to allow specific resource classes to be deleted without a prompt. The grant is tied to the lifetime of the wrapping process and auto-reverts to the safe default the moment the child exits.

This is a safety rail, not a security boundary: the child runs as the same OS user, so a true guarantee must come from the API token scope server-side. The mechanism is engineered for ephemerality, liveness, and deliberate human opt-in.

How it works

# Allow an agent to delete workspaces and runs for one session:
tfctl harness exec --allow-delete=workspaces,runs -- opencode

# Inside that session, nested tfctl deletes for those classes succeed noninteractively:
tfctl api /workspaces/ws-123 -X DELETE
  • harness exec writes a short-lived session file (~/.config/tfctl/exec/<token>.hcl, mode 0600) recording the granted classes + creator PID, takes an exclusive lock, and sets TFCTL_EXEC_SESSION=<token> in the child's environment.
  • On DELETE, tfctl api resolves the resource class from the path and consults the session: it must exist, be live, and the requesting process must be a descendant of the session creator (ancestry walk) so a leaked env var can't re-authorize an unrelated process.
  • When the child exits, the session file is unlocked and removed; the default (interactive confirmation) is restored automatically.

Permission model

  • --allow-delete is repeatable and accepts comma-separated values.
  • Special tokens reversible / all cover any reversible class but never cover the irreversible classes organizations and projects must always be named explicitly (--allow-delete=projects).
  • Unauthorized noninteractive deletes fail with a self-documenting message that prints the exact harness exec --allow-delete=<class> command to authorize, or the -X DELETE command to run interactively.

Testing

  • unit tests for execsession, harness exec, and the api DELETE gate (including a fake Authorizer).
  • Manual end-to-end smoke: child sees the live token + 0600 session file; the file is removed on exit; --dry-run creates no session and prints the would-do message.

Example Output

Example

Authorized session: a human wraps the agent; nested deletes for the granted classes succeed without a prompt, and the grant disappears when the child exits:

$ tfctl harness exec --allow-delete=workspaces,runs -- bash
WARNING: tfctl deletes enabled for this session: [workspaces runs]

# ...inside the wrapped process, a nested tfctl delete is now noninteractive:
$ tfctl api /workspaces/ws-abc123 -X DELETE
WARNING: deleting /workspaces/ws-abc123 (authorized by exec session)

$ exit          # session ends → permission auto-reverts to the safe default

Default (no session): noninteractive deletes are refused with a self-documenting message:

$ tfctl api /workspaces/ws-abc123 -X DELETE --quiet
ERROR: refusing to DELETE /workspaces/ws-abc123 in non-interactive mode: no active session permission for resource class "workspaces".

A human can authorize deletes of "workspaces" for one session by wrapping the agent:
  tfctl harness exec --allow-delete=workspaces -- <command>

Or run the delete yourself in an interactive terminal:
  tfctl api /workspaces/ws-abc123 -X DELETE

Irreversible classes are never covered by wildcards: even inside a session granting all, deleting a project requires an explicit grant:

$ tfctl harness exec --allow-delete=all -- ./ci.sh
WARNING: tfctl deletes enabled for this session: [all]
# ...nested:
$ tfctl api /projects/prj-xyz -X DELETE --quiet
ERROR: refusing to DELETE /projects/prj-xyz in non-interactive mode: no active session permission for resource class "projects".

A human can authorize deletes of "projects" for one session by wrapping the agent:
  tfctl harness exec --allow-delete=projects -- <command>
...

Dry run evaluates the gate but creates no session and sends no request:

$ tfctl harness exec --dry-run --allow-delete=all -- ./ci.sh
DRY RUN: would create exec session (allow-delete=[all]) and run: ./ci.sh

PR Checklist

  • Run npx changie new or install changie to prepare a new changelog entry for the next set of release notes.
  • Ensure any command changes are sensitive to these global flags:
    • --json — Force machine readable output to stdout. Does not apply to stderr.
    • --markdown — Force markdown output to stdout. Does not apply to stderr.
    • --dry-run — Don't make any actual writes or other mutations. Describe what would have changed to stderr.
    • --quiet — Don't render output to stdout.
  • Get the logging interface from the context and add debug logging for interesting conditions and nonfatal situations.
  • Run make gen/screenshot if the root command output changes.
  • Add the Autocomplete field to positional arguments and flags to assist shell autocomplete.

PCI review checklist

  • I have documented a clear reason for, and description of, the change I am making.

  • If applicable, I've documented a plan to revert these changes if they require more than reverting the pull request.

  • If applicable, I've documented the impact of any changes to security controls.

    Examples of changes to security controls include using new access control methods, adding or removing logging pipelines, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant