Skip to content

feat: standing grants (part 1 of #110) - #115

Merged
groupsky merged 18 commits into
mainfrom
feat/standing-grants-110
Oct 29, 2026
Merged

groupsky merged 18 commits into
mainfrom
feat/standing-grants-110

Conversation

@groupsky

@groupsky groupsky commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Part 1 of #110 (standing grants). Refs #110.

The design is in the plan comment on #110 and the owner answers of 2026-10-29 (A: dropped at a broker stop, B: an alarm ends the grant, C: secrets only, D: Renew as proposed).

Part 1: core kind, windows, config, request, token, rotation, the use path and the use alarms, the audit events, DESIGN.md and THREAT-MODEL.md.
Part 2 (own PR): missed-window alarm, warning and Renew, weekly summary, /grants, docs.

Progress

  • 1. DESIGN.md and THREAT-MODEL.md: the grant core and standing grants (the spec for the next steps)
  • 2. config: rule flag standing = true and the [standing] section
  • 3. approval core: grant kind, windows, uses per window
  • 4. token: nkg_ prefix, random, checksum, SHA-256 hash only on the broker
  • 5. api and client: nodkey preapprove --daily/--weekly, token file; nodkey run --grant-token FILE with a safe save of the new token
  • 6. broker use path: token check, rotation, alarms (old token, second use, outside the windows, unknown token), audit events
  • 7. telegram: the standing prompt and the alarm messages (with sound)
  • 8. security review of the whole part, Go 1.27 and 1.26, stress tests, ready

Step 5a done: api Window, PreapproveRequest.Windows, PreapproveAnswer.GrantToken, GrantTokenHeader; client SaveGrantToken, CheckGrantTokenFile. Step 5b done: SecretsAnswer.GrantToken, nodkey run --grant-token (bad token file exits 64, missing or bad new token 69, failed save 77). Step 6a done (cf4b0f8): the broker makes standing grants; overlapping windows are denied; audit has kind standing, Subject.Windows, end causes alarm and renewed. Step 6b done: the use path, rotation and the alarms (grant_alarm, new_token_hash); the alarm texts and the standing use notice are in internal/telegram (step 7 done with it). Choices to check in the review: outside_window alarms at most once per calendar day (DESIGN.md says so now); unknown_token alarms capped at 3 per minute per client; alarms go to the notice chat, with sound.

Step 8a done (ae944af, 136a570): tests for an alarm through the fake Telegram server (TestBrokerStandingAlarmThroughTelegram), a failed secret_sent write (TestStandingSecretSentFailure), a cancel after rotation (TestStandingCancelAfterRotation, TestRunGrantTokenCancel). golangci-lint: 0 issues (8 fixed with //nolint and a reason). Stress -count=20 -cpu 1,2,4 clean.

Step 8b done: security review of the whole part. Findings (full text outside the repo, summary here):

  • M1 CancelGrant ends only scheduled grants, so --cancel, dropGrant and the client's cancelLeftover leave a standing grant live. Fix: end standing grants too, cause cancelled, DESIGN.md, tests.
  • L2 readGrantToken: Lstat then ReadFile follows a symlink. Fix: open with O_NOFOLLOW, check the open file.
  • L3 another client's token counts as unknown; the owner's grant stays live. Fix: match across clients, end that grant with an alarm.
  • L4 the once-a-day outside_window mark is set before the send; a use before the start has no alarm. Fix: mark after a good send; before the start is outside_window.
  • docs/setup.md: keep the token file in a private folder (the reviewer's answer to the open finding below: no code fix needed).

Step 8c done: findings fixed (4c2254e, 690ccf8, 3851b8d, ba51f50, 272ffe4).

Step 8d done: review of the fix diff found 2 low: the foreign token alarm did not name the owner machine; a named pipe in place of the token file made the open wait. Both fixed in 464e786.

Step 8e done: review of 464e786: no findings. Go 1.27 and Go 1.26 with CI=true: all pass; go vet and golangci-lint: 0 issues; standing grant tests stressed -count=20 -cpu 1,2,4: clean. Ready.

Next: squash merge on green CI, then part 2 on feat/standing-grants-110-part2.

Open review findings: none. (The token file folder: no code fix; docs/setup.md says to keep it in a private folder.)

🤖 Generated with Claude Code

groupsky and others added 7 commits October 29, 2026 07:05
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@groupsky
groupsky force-pushed the feat/standing-grants-110 branch from 94a4bc7 to 6cbb427 Compare October 29, 2026 05:20
groupsky and others added 11 commits October 29, 2026 07:28
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion (#110)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…w only after a sent alarm (#110)

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@groupsky
groupsky marked this pull request as ready for review October 29, 2026 10:28
@groupsky
groupsky merged commit bfd9036 into main Oct 29, 2026
9 checks passed
@groupsky
groupsky deleted the feat/standing-grants-110 branch October 29, 2026 10:28
@groupsky groupsky mentioned this pull request Oct 29, 2026
4 of 7 tasks
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