Skip to content

feat: platform-token auth for runs, run help text, nan --version fix (0.1.25) - #42

Merged
sre-helmcode merged 9 commits into
mainfrom
feat/token-auth-and-help
Oct 10, 2026
Merged

sre-helmcode merged 9 commits into
mainfrom
feat/token-auth-and-help

Conversation

@sre-helmcode

@sre-helmcode sre-helmcode commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Merging this publishes v0.1.25. internal/tui/tui.go bumps Version to 0.1.25, and tag.yml tags and releases it automatically once CI passes on main.

What

Makes the CLI usable headless (CI, servers) with platform tokens, documents what a run does, and fixes nan --version.

1. Token auth for nan run / nan runs

Precedence (first match wins), documented in nan run --help and the README:

  1. NAN_TOKEN: a nan_pat_... platform token or an sk-... API key. Read only, never written to disk.
  2. --token-file PATH (on nan run and persistent on nan runs): first line of the file. On unix a regular file accessible to group/others (mode & 0o077) is refused with chmod 600 advice (writable-by-others also matters: it would let another user swap in their token and receive your runs/prompts).
  3. Platform token saved with nan auth login --api-token.
  4. Session from nan auth login.
  5. API key saved by the Setup tab.

The saved platform token ranks above the session (the brief listed session > stored apiKey, which still holds): a session expires on the server with nothing local knowing, so ranked after it a token saved for runs would never be reached on a machine that once signed in by email.

Tokens are shape-checked (nan_pat_/sk- prefix, printable ASCII, <= 512 bytes) before being sent, so nothing can split a header. Errors name the credential's source ("the token in NAN_TOKEN", "the token in /path"), never its value. A refused token says where to make a new one.

nan auth login --api-token reads the token from stdin only (hidden prompt when stdin is a real terminal per term.IsTerminal, first line otherwise, so </dev/null gives "no token arrived"), verifies it with GET /v1/runs?limit=1, and saves it through the existing session.Save (temp file + 0600 + rename). Nothing is saved on any failure.

Deviation from the brief, on purpose: a nan_pat_ token is saved in a new platformToken field, not in apiKey. The Setup tab writes apiKey into every tool config it manages (OpenCode, Pi, Codex...), and the inference API rejects platform tokens, so storing a PAT there would break those tools. sk- keys still go to apiKey.

nan me and nan metrics usage now say "<cmd> needs a session: run nan auth login (platform tokens and API keys only work for nan run and nan runs)" when only a token is configured, instead of a bare 401. The dashboard's sign-in step shows the same notice when a platform token (saved or NAN_TOKEN) is present. --ws default resolution keeps using GET /api/workspaces, which accepts tokens.

2. Help text

  • nan run --help: sections for Git branch (auto / --worktree / --no-worktree, nan-run/<8 chars>, never pushes), Queue, portal label mapping, Authentication, docs link https://nan.builders/docs/runs. Flag descriptions for --worktree/--no-worktree/--cwd match.
  • nan runs --help: one paragraph + docs link.
  • nan runs logs --help: event types, --json prints one event per line, schema in the docs.
  • nan auth login --help: explains --api-token.

3. nan --version / nan -v

Prints nan <version> and exits 0 (the framed full-screen wordmark read as the dashboard opening). The now-unused tui.WelcomeStacked is removed.

4. Docs

README: runs section (branch, queue, portal labels), new Authentication section with a GitHub Actions example using NAN_TOKEN from a secret. SECURITY.md: where a saved platform token lives.

Tests

Spec-first, httptest mocks:

  • cmd/credentials_test.go: precedence matrix, shape validation (incl. CRLF/header injection), token file first line / errors / private-mode enforcement, bearer vs cookie on the wire, refused-token messages per source, end-to-end NAN_TOKEN and --token-file through nan runs ls, NAN_TOKEN never written to disk, session-only commands explain tokens, no token value in any error.
  • cmd/auth_test.go: --api-token saves verified PATs to platformToken and sk- to apiKey with 0600, keeps the rest of the session, hidden read on a TTY, nothing saved on empty/malformed/401/5xx, flag combination refused.
  • cmd/root_test.go: --version and -v print nan <version> and never open the TUI.
  • cmd/help_test.go: required facts in help, docs links, no em-dashes, --token-file on every runs command.
  • internal/tui/session_test.go: sign-in notice with a platform token.

go vet ./... and go test ./... pass locally (macOS); CI is authoritative for Linux.

Deploy notes

No env vars or migrations. Release is automatic on merge (v0.1.25).

🤖 Generated with Claude Code

barckcode and others added 9 commits October 10, 2026 21:37
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Precedence: NAN_TOKEN > --token-file > session > saved platform token >
saved API key. A token file readable or writable by others is refused.
Refused credentials are named, never shown. nan me and nan metrics usage
explain that they need a session when only a token is configured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reads a platform token or API key from stdin (hidden on a terminal),
checks it with GET /v1/runs?limit=1 and saves it 0600. Platform tokens
go to platformToken, never apiKey, which the Setup tab writes into tools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…okens

nan --version and nan -v print "nan <version>" and exit 0 instead of
the full-screen wordmark. The sign-in step of the dashboard says that a
platform token only works for nan run and nan runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n --help

nan runs --help and nan runs logs --help link the docs; logs lists the
event types and the --json line format.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A saved platform token now goes before the session: a session expires
  on the server unseen, and ranked after it the token was never reached.
- --api-token asks the terminal itself whether stdin is one, so
  </dev/null (cron, docker run without -i) says no token arrived.
- The login output says what nan run will actually use, and warns when
  an sk- key replaces the one the Setup tab saved.
- --token-file paths are sanitised in errors; a missing file says so.
- NAN_TOKEN is one constant (session.TokenEnvVar); usingKey() removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stat and read errors drop the raw path from *os.PathError too. Help and
README say an sk- key saved with --api-token counts as a saved API key,
and a refused saved token says the logout way back to the session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sre-helmcode
sre-helmcode merged commit 589feca into main Oct 10, 2026
5 checks passed
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.

2 participants