Add Nous Portal provider - #3376
Conversation
|
🦞👀 Pull request received. I will update this pull request when review starts. |
|
Codex review: blocked before merge. Reviewed September 3, 2026, 10:58 PM ET / September 4, 2026, 02:58 UTC. ClawSweeper reviewWhat this changesThe PR adds a disabled-by-default Nous Portal provider that reuses a Hermes OAuth access token to show subscription credits, renewal timing, plan, and purchased-credit balance in CodexBar. Merge readiness⛔ Blocked before merge - 3 items remain Keep this PR open for maintainer sign-off: the prior changelog finding is fixed, the implementation and real CLI evidence are strong, but the new Hermes OAuth/file-format contract needs an explicit product and maintenance owner. Priority: P2 Review scores
Verification
How this fits togetherCodexBar registers provider descriptors for the menu bar and CLI, then resolves provider credentials and fetches usage snapshots for display. This adds Nous Portal as a first-party provider using a Hermes-managed OAuth credential and the portal account endpoint. flowchart LR
A[Hermes login or environment token] --> B[Credential resolution]
B --> C[Trusted portal selection]
C --> D[Nous account request]
D --> E[Usage and credit snapshots]
E --> F[CodexBar menu bar and CLI]
Decision needed
Why: The implementation follows the provider architecture and includes direct proof, but VISION.md requires sign-off for provider-auth and unclear privacy/maintenance contracts. Before merge
Agent review detailsSecurityNone. Review metrics
Root-cause clusterRelationship: Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Obtain explicit sponsorship for the Hermes and Nous contract, then land the disabled-by-default provider with its existing credential-boundary tests and documentation. Do we have a high-confidence way to reproduce the issue? Not applicable as a feature request; the PR body nevertheless provides high-confidence redacted CLI traces for the implemented provider and its credential-boundary cases. Is this the best way to solve the issue? Yes, conditionally: the descriptor and shared transport approach fit current provider patterns, but maintainers must first accept ownership of the Hermes and Nous contract. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 476573dddc9f. LabelsLabel justifications:
EvidenceWhat I checked:
Likely related people:
Rank-up movesOptional improvements that raise the rating; they are not merge blockers.
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
HistoryReview history (6 earlier review cycles)
|
1573715 to
ec8534d
Compare
Show Nous Portal monthly subscription credits, cycle reset, plan, and purchased credit balance by reusing the Hermes Agent OAuth login stored in ~/.hermes/auth.json. The provider reads GET /api/oauth/account with the existing access token and never calls the refresh endpoint, because Nous refresh tokens are single-use and reuse revokes the Hermes session. Refs steipete#1367 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…op-up - Only send the Hermes bearer token to nousresearch.com hosts (or an explicit NOUS_PORTAL_BASE_URL override). An untrusted stored portal_base_url is ignored, logged, and reported as rejectedStoredHost in the verbose trace. - Reject an expired NOUS_PORTAL_ACCESS_TOKEN before any request, and keep the strategy available so the fetch reports the specific expiry reason. - Always attach the purchased (top-up) balance as credits and show it in the plan row; label detail rows to match the portal billing page. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ses only The app renders the fetch diagnostic as a warning line, so the routine "portal=… credential=…" trace note now only appears in verbose runs or when an untrusted stored portal host was ignored. Keep the plan row to the plan name; the top-up balance lives in the Credits section. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
When HERMES_HOME is set, only that root's auth.json and shared/nous_auth.json are consulted; the default ~/.hermes root is never used as a fallback, so a missing, invalid, or expired custom profile reports its own error instead of silently querying with another profile's token. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
11b9cef to
c4ac4b3
Compare
Reject every non-HTTPS portal base URL, loopback included, so the Hermes bearer token can never be sent in cleartext. Add a request-level test that the Authorization header only targets https://portal.nousresearch.com when an http://127.0.0.1 override or an untrusted stored host is supplied. Document that the plan row shows the plan name only and the top-up balance lives in the Credits section. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The merge from main resolved CHANGELOG.md in favor of the branch, which dropped the current Unreleased Fixed bullets and the whole 0.56.4 section. Take main's changelog verbatim and add the Nous Portal entry under a new Added heading alongside it, so the diff against main is purely additive.
|
Fixed the P1 changelog finding in 6b655b4. The earlier The diff against No Swift files changed in this commit. That leaves only the maintainer product decision: whether CodexBar wants to adopt a disabled-by-default Nous Portal provider. Happy to keep it out-of-tree as a plugin instead if that fits the project better. |
Adds a disabled-by-default Nous Portal provider showing monthly subscription credits, cycle reset, plan (plan row shows the plan name only), and the purchased (top-up) credit balance in the Credits section. Refs #1367.
How it authenticates
Nous Portal's account and billing endpoints only accept the OAuth access token minted by the Hermes Agent device-code login (API keys are inference-only, per https://portal.nousresearch.com/api-docs). CodexBar reuses that token from
~/.hermes/auth.json(or$HERMES_HOME/auth.json, or~/.hermes/shared/nous_auth.json) and never calls the refresh endpoint: Nous refresh tokens are single-use and the portal revokes the whole session on reuse, so a second refresher would log Hermes out. An expired token yields a "runhermes" error instead.One request per refresh:
GET {portal}/api/oauth/account.Token destination boundary (addresses the P1/P2 findings)
The bearer token is sent to exactly one origin, resolved in this order:
NOUS_PORTAL_BASE_URL/HERMES_PORTAL_BASE_URL(operator-set; HTTPS only, plain HTTP is refused for every host including loopback and the default portal is used instead).portal_base_urlfrom the Hermes auth file, only if its host isnousresearch.comor a subdomain.https://portal.nousresearch.com.A stored host outside that policy is never used: it is replaced by the default portal, logged as a warning, and reported as
rejectedStoredHost=<host>in the verbose trace. Expired tokens from either the auth file orNOUS_PORTAL_ACCESS_TOKENare rejected before any request is created (NousUsageError.sessionExpired/.environmentTokenExpired).Credential root (addresses the HERMES_HOME finding)
HERMES_HOMEis the exclusive credential root. When it is set, only$HERMES_HOME/auth.jsonand$HERMES_HOME/shared/nous_auth.jsonare consulted;~/.hermesis never appended as a fallback. A missing, invalid, or expired custom profile therefore reports its own error (missingCredentials,authFileInvalid(path),sessionExpired(path)) instead of silently using another profile's token. Tests cover all three cases.Real behavior proof (redacted CLI traces)
A. Real Hermes login, trusted origin
B. Same real login, auth file copied to a temp
HERMES_HOMEwithportal_base_urltampered tohttps://stored.exampleThe token reached
portal.nousresearch.comonly;stored.examplereceived nothing.C. Expired
NOUS_PORTAL_ACCESS_TOKEN(JWT withexpin 2000)No network request is made in this case.
D. Explicit
HERMES_HOMEpointing at an empty custom profile, while the default~/.hermeslogin is validThe default profile's token was not used.
Tests
swift test --filter 'Nous|ProviderArchitectureGatekeeper'(67 tests) passes, including a request-level test that theAuthorizationheader only ever targetshttps://portal.nousresearch.comwhen anhttp://127.0.0.1override or astored.exampleauth-file host is supplied, refusal of every HTTP override (loopback included), exclusiveHERMES_HOMEresolution (missing, invalid, expired custom profile), trusted-host acceptance (portal.nousresearch.com,preview.portal.nousresearch.com), rejection ofstored.exampleand the look-alikenousresearch.com.evil.example, env-override precedence, expired env/auth-file tokens, auth-file shape parsing, payload mapping, and the architecture gatekeeper.Docs:
docs/nous.md,docs/providers.md, changelog.🤖 Generated with Claude Code