Skip to content

No-paste credential terminal for the paper lane: hidden-prompt store, read-only paper rate-limit probe - #203

Merged
seathatflowsinourveins merged 3 commits into
mainfrom
claude/credential-terminal-20260924
Sep 24, 2026
Merged

seathatflowsinourveins merged 3 commits into
mainfrom
claude/credential-terminal-20260924

Conversation

@seathatflowsinourveins

Copy link
Copy Markdown
Collaborator

Seamless, no-paste credential practice for the paper lane. Keys go from the operator's keyboard straight into the per-host store. They never pass through an agent, a chat, a command line or shell history.

Tools (tools/credentials/)

  • open_credential_terminal.sh <entry-id> [--probe] opens a new terminal window: Windows Terminal on WSL2, a console window as fallback, Terminal.app on macOS, or an X terminal on Linux. If none is available, it prints the one command to run. The window runs a generated session script that holds commands only and deletes itself on exit. Before anything is typed, the script:

    • prints the checkout commit;
    • refuses if the credential tools have uncommitted changes;
    • clears PYTHONPATH, LD_PRELOAD, BASH_ENV and similar variables;
    • runs Python with -I.

    Paths and the distro name are validated before they reach cmd.exe or wt.exe, the window starts with wsl.exe -u <user> --exec, and a failed launch is reported.

  • set_credential.py <entry-id> accepts only operator-supplied stored entries (required, optional or paid) whose file sits directly in the store.

    • It reads each value with hidden input, and refuses when the terminal cannot hide input.
    • It writes export NAME=value lines, the documented format that every parser accepts.
    • The file is 0600 in the 0700 store, written through an O_DIRECTORY|O_NOFOLLOW handle.
    • Replacing an existing file needs a hidden replace.
    • A value with ~, non-ASCII or shell-special characters is refused.
  • alpaca_rate_limit_probe.py is a paper-only read-only probe.

    • It sends one GET /v2/account to the fixed paper host, follows no redirect and reads no body.
    • It reports only the HTTP status and the x-ratelimit-* headers, writing any result file via a temp file and replace.
    • It checks the key file after opening it: same inode, owner, exactly 0600, one link, and a 0700 directory.
    • Agents may run it with --env-file "$PAPER_ENV_FILE".

Scope. Live broker keys are out of scope for this repository; live trading is handled outside it. An earlier draft of this branch had a live mode, and the independent review's high finding on it has been removed together with the mode. The alpaca-paper inventory note records the scope.

Verification

  • 51 tests (tests.test_credential_tools, tests.test_credential_status, tests.test_secret_path_guard), run with fake values only. They include:
    • an end-to-end check with two local HTTP servers that redirects are never followed;
    • a check that a result path which is a symlink is replaced, not followed;
    • the hard-link, mode, directory and worktree refusals;
    • the launcher dry run and argument checks.
  • validate.py and evidence_manifest.py --check pass; only the sha256 and byte counts of the two changed registered files were updated.
  • gitleaks: no leaks.
  • Native check: on this host's dedicated paper account, the probe returned HTTP 200 with x-ratelimit-limit: 200 (199 remaining).

Review. The first independent review raised 1 high, 2 medium and 12 low findings, all addressed. The high one was live keys at rest, now removed with the live scope. A follow-up review of the redesign is in progress; its findings will be applied before merge.

🤖 Generated with Claude Code

Scout and others added 2 commits September 24, 2026 13:07
…e limits read-only; live keys never stored

tools/credentials/open_credential_terminal.sh opens a new terminal window (Windows
Terminal on WSL2, Terminal.app on macOS, an X terminal on Linux) for credential work
that never passes through an agent, chat, command line or shell history.
- set_credential.py stores operator-supplied inventory entries: hidden input (refuses
  when input cannot be hidden), 'export NAME=value' lines, 0600 file in the 0700 store
  via an O_NOFOLLOW directory handle, hidden 'replace' confirmation.
- alpaca_rate_limit_probe.py sends one read-only GET /v2/account to the fixed host, no
  redirects, no body read, and saves only status + x-ratelimit-* headers.
- Live keys are never stored (alpaca-live is interactive_only): --live-rate-limit types
  the pair for one probe and discards it; the probe refuses --env-file for live and
  refuses without a terminal.
- The window prints the checkout commit and refuses if the credential tools have
  uncommitted changes; it clears PYTHONPATH/LD_PRELOAD/BASH_ENV and runs python3 -I.
Independent review findings (1 high, 2 medium, 12 low) addressed; 52 tests.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…tside this repository)

Removes the live rate-limit mode and the alpaca-live inventory entry. The probe is
paper-only (fixed paper host, stored alpaca-paper key; agents may run it with
--env-file "$PAPER_ENV_FILE"). The launcher stores operator-supplied entries with
hidden prompts and --probe applies only to alpaca-paper. Verified: the paper probe
reports x-ratelimit-limit 200 (HTTP 200) on this host's dedicated paper account.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

…e checker exit, launcher order)

- set_credential.py and alpaca_rate_limit_probe.py re-exec themselves with python3 -I
  when run directly, so a poisoned PYTHONPATH cannot shadow getpass/urllib (test plants
  fake modules and asserts none is imported).
- credential_status.py exits 1 when ANY existing credential file is unsafe (not only
  required ones); unsafe_stored added, unsafe_required kept for consumers; missing
  optional entries stay informational (tested).
- Launcher validates paths before creating directories, warns when not in a git checkout,
  and no longer claims a backgrounded X-terminal launch was confirmed.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@seathatflowsinourveins

Copy link
Copy Markdown
Collaborator Author

Follow-up independent review (redesign): no high or medium findings remain. It confirmed that no tool prints a stored value and that every finding from the first round is resolved. Its new low and info items are handled in 80045db:

  • Both tools re-exec themselves with python3 -I when run directly. A test plants fake getpass.py and argparse.py on PYTHONPATH and asserts that neither is imported.
  • credential_status.py exits 1 when any existing credential file is unsafe, not only a required one. Missing optional entries stay informational (tested), and unsafe_required is kept for existing consumers.
  • The launcher validates paths before creating directories, warns outside a git checkout, and no longer claims an unconfirmed X-terminal launch succeeded.
  • The paper probe being runnable by agents is intended and documented; paper keys are agent-usable through $PAPER_ENV_FILE.

The live mode reviewed in round 1 was removed entirely in 3fbcd9f: live is out of scope for this repository. Tests: 132 OK, run CI-style.

@seathatflowsinourveins
seathatflowsinourveins merged commit 0a47a1c into main Sep 24, 2026
22 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/credential-terminal-20260924 branch September 24, 2026 18:56
seathatflowsinourveins added a commit that referenced this pull request Sep 24, 2026
… fill-precision branch

Conflicts were registries and counts only: took main's copies, regenerated the
rate-limit evidence (1693 trading, 16 data headers), re-pinned this branch's five
sources in source-hashes.json and re-registered its files in the evidence manifest.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
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