Skip to content

fix(gateway,skills): persist pairing tokens and skills with atomic replace - #98

Merged
adriannoes merged 3 commits into
developmentfrom
fix/bugbot-batch-auth-skills
Sep 21, 2026
Merged

adriannoes merged 3 commits into
developmentfrom
fix/bugbot-batch-auth-skills

Conversation

@adriannoes

Copy link
Copy Markdown
Collaborator

Summary

  • Rewrite of pairing-token and skill persist integrity against current development (not a cherry-pick of the Cursor drafts).
  • auth_pair writes auth_tokens.json via unique temp (mkstemp)+fsync+rename so O_TRUNC cannot wipe existing tokens, and fails closed when bearer RNG fails (pairing code kept).
  • skill_create / skill_update persist markdown the same way so fopen("w") cannot wipe an existing skill.

Supersedes #71, #92, #77.

Does not mix file/camera, cron, shell, asap, config, or sandbox.

Test plan

  • make test_auth / ./build/test_auth
  • make test_skill / ./build/test_skill
  • CI=true make test
  • make static

Made with Cursor

Write auth_tokens.json via unique temp+fsync+rename so O_TRUNC cannot
wipe existing pairing tokens on ENOSPC or crash. Fail closed when
bearer RNG fails so pairing does not persist an uninitialized token.

Refs: #71, #92
Write skill markdown via unique temp+fsync+rename so fopen("w") cannot
wipe an existing skill on ENOSPC or crash.

Refs: #77

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

ShellClaw is a C99 edge agent (Jetson / Raspberry Pi) with a ReAct loop, gateway pairing auth, and markdown skills under ~/.shellclaw/skills/. This PR delivers the persist-integrity fixes for those two stores: pairing tokens no longer die to O_TRUNC, and skill updates no longer die to fopen("w") truncate-before-write. auth_pair also fails closed when bearer RNG fails. The persist path matches write_file_atomic in src/tools/file.c (unique mkstemp, fsync, rename, unlink on failure). I ran CI=true gcc -Werror test_auth and test_skill (both pass, linked -lm) and the same two binaries under ASan+UBSan (both pass). GitHub static, test, release was still pending at review time.

Must Fix

None.

Should Fix

  1. Third copy of the atomic-write helper. write_tokens_atomic (src/gateway/auth.c:199) and write_skill_atomic (src/core/skill.c:405) are the same function as write_file_atomic. The next durability change (directory fsync, EINTR retry) will have to land in three places. Extract a shared helper in a follow-up. This PR is right not to mix that into the bugfix.

  2. /pair treats persist and RNG failure as a bad pairing code. handle_pair in src/gateway/routes.c (unchanged here) maps every auth_pair non-zero return to HTTP 400 "Invalid pairing code" and auth_pair_record_failure (5 strikes, 300s). RNG failure is newly a -1, which is the correct fail-closed behavior, but a valid code plus a full disk or dead urandom now burns lockout. Distinct return codes for invalid code vs operational failure would avoid that.

  3. Skill files become mode 0600. mkstemp creates 0600. file.c then fchmods 0644. This copy does not, so skill_create / skill_update stop using fopen("w") umask (typically 0644). Fine for a single-user agent. If anything else reads skills/, it loses group/other read after the first save.

Nice to Have

  • Explicit fchmod(fd, 0600) plus a st_mode & 0077 check on auth_tokens.json, matching manifest_keys.c. glibc mkstemp is already 0600; this would make the credential mode explicit rather than an implicit libc property.
  • fsync the parent directory after rename if the crash claim should cover a power loss before the dirent is journaled. file.c does not do this either.
  • src/core/skill.c is 532 lines after this change (was under 500). Not a merge gate; the in-tree line cap that is actually enforced is the 1000-line config.c rule.
  • Assert auth_tokens.json is 0600 after a successful pair.

Positive Highlights

  • Unique .sc-auth-XXXXXX / .sc-skill-XXXXXX sidecars avoid the predictable path.tmp sibling clobber left open on #94.
  • Skill loader already skips d_name[0] == '.', so in-flight temps are not injected into the system prompt.
  • Pairing code is cleared only after a successful persist (src/gateway/auth.c:288-289). The urandom test checks leftover token_out, no tokens file, and a retry with the same code.
  • RLIMIT_FSIZE tests fail the write, assert the previous content still validates, then succeed after the limit is restored. That is the actual bug, not a coverage bump.
  • CHANGELOG lists #71/#92 under Security and #77 under Fixed. Two conventional commits, scoped away from file/cron/sandbox.
Open in Web聽View Automation聽

Sent by Cursor Automation: Adrianno鈥檚 personal code review

Comment thread src/gateway/auth.c
Comment thread src/core/skill.c
@adriannoes
adriannoes merged commit 87d4709 into development Sep 21, 2026
2 checks passed
@adriannoes
adriannoes deleted the fix/bugbot-batch-auth-skills branch September 21, 2026 20:48
adriannoes added a commit that referenced this pull request Sep 21, 2026
Bring in #96 shell hang fix and #98 atomic auth/skill persist.
Keep Unreleased changelog entries from all three batches.
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