Skip to content

feat(proxy): add redeem_phantoms for by-value nonce redemption - #1469

Open
kipz wants to merge 1 commit into
nolabs-ai:mainfrom
kipz:kipz/redeem-phantoms-upstream
Open

feat(proxy): add redeem_phantoms for by-value nonce redemption#1469
kipz wants to merge 1 commit into
nolabs-ai:mainfrom
kipz:kipz/redeem-phantoms-upstream

Conversation

@kipz

@kipz kipz commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Linked Issue

Closes #1468

Summary

Adds redeem_phantoms to a custom_credentials route — a list of broker
credential names the route resolves from a caller-presented phantom in a request
header (by-value / proof-of-possession). A non-empty list forces interception for
the route's host; resolution is gated by credential name (not value); it composes
with a managed credential_key/auth or stands alone. JWT-shaped phantoms are
whole-token replaced (anchored to the parsed nonce) so a real JWT stays 3 segments.
The broker gains a name-gated resolve_nonce_for_credentials (per-nonce name map)
and the NonceResolver trait gains a resolve_for_credentials method (default None).

Stacked — do not merge until #1443 lands in main:

Test Plan

make ci clean. New unit tests: shape-aware JWT resolution (3-segment result),
opaque substring replacement, route-authoritative name gate + fail-closed,
name-vs-value across per-audience overwrites, raw-value reissue relabel, a
multibyte-boundary panic regression, and profile validation (composes with
credential_key; rejected with aws_auth/spiffe; empty-name rejected).

Checklist

  • An issue exists and is linked above
  • All commits are signed-off, using DCO
  • All new code follows the project's coding standards (CLAUDE.md) and is covered by tests
  • Public-facing changes are paired with documentation updates
  • Release note has been added to CHANGELOG.md if needed

Agent Disclosure

Generated by an AI agent. Reuses the existing NonceResolver and CONNECT intercept
dispatch; no new resolver trait, URI scheme, or consumer class. Files changed:
crates/nono-proxy/src/{config,route,server,token,tls_intercept/{handle,h2_forward}}.rs,
crates/nono-cli/src/{network_policy,profile/mod,proxy_runtime,tool-sandbox/token_broker}.rs,
crates/nono-cli/data/nono-profile.schema.json.

Agent Compliance Check

  • I am not prohibited from contributing under this policy
  • An issue already exists (Route-gated by-value resolution of caller-presented capture nonces #1468)
  • I disclosed that I am an agent in the issue discussion
  • I described my intent and approach in the issue discussion
  • I reviewed repository coding and security rules for the affected area
  • I provided required attribution for reused or adapted code
  • I did not use forbidden patterns such as unwrap/expect
  • I used NonoError where required
  • I validated and canonicalized all relevant paths
  • This PR matches the approved or disclosed issue scope

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

PR Review Summary

Size

Metric Value
Lines added +953
Lines removed -81
Total changed 1034
Classification Large (> 300 lines)

Affected crates

  • crates/nono-proxydownstream consumers depend on this crate. API or behaviour changes will affect external callers; treat any breaking change with extra scrutiny.
  • crates/nono-cli — CLI changes. Verify argument parsing, flag documentation, and UX behaviour across supported platforms.

Blast radius — Moderate

This PR touches: source code,configuration / policy files


Updated automatically on each push to this PR.

@kipz
kipz force-pushed the kipz/redeem-phantoms-upstream branch from 8800d6c to e034baa Compare July 21, 2026 11:11
@kipz
kipz marked this pull request as ready for review July 21, 2026 13:12

@nogent-nolabs-ai nogent-nolabs-ai 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.

nogent code review

No blocking issues; 0 suggestions.

Automated code + security review. CI already covers clippy, rustfmt, tests, cargo-audit and commit-lint.

Comment thread crates/nono-proxy/src/tls_intercept/h2_forward.rs
Comment thread crates/nono-proxy/src/tls_intercept/websocket.rs
Comment thread crates/nono-proxy/src/tls_intercept/handle.rs
kipz added a commit to kipz/nono that referenced this pull request Jul 21, 2026
Address nogent review feedback on nolabs-ai#1469:

- websocket.rs: read_response used unbounded read_line for both the
  status line and header lines, so an upstream that never sends a
  newline could grow the buffer without limit until OOM or the 30s
  timeout. Add a bounded line reader (8KB/line) that fails fast.

- h2_forward.rs: handle_h2_stream hardcoded websocket_path: None,
  skipping the upgrade_rules gate for RFC 8441 extended CONNECT
  streams. Detect :protocol: websocket and route it through the same
  check as the HTTP/1.1 Upgrade path, so enabling h2 WebSocket
  tunneling later can't silently bypass the allowlist.

Signed-off-by: James Carnegie <me@kipz.org>
kipz added a commit to kipz/nono that referenced this pull request Jul 21, 2026
kipz added a commit to kipz/nono that referenced this pull request Jul 21, 2026
kipz added a commit to kipz/nono that referenced this pull request Jul 21, 2026
Integration fixup: nolabs-ai#1469/nolabs-ai#1443 added CredentialRouteDef.upgrades;

Signed-off-by: James Carnegie <me@kipz.org>
nolabs-ai#1476's vault test literal predates it. Combined-branch only.
kipz added a commit to kipz/nono that referenced this pull request Jul 23, 2026
…e nonce redemption

# Conflicts:
#	crates/nono-cli/src/proxy_runtime.rs
#	crates/nono-cli/src/tool-sandbox/token_broker.rs
#	crates/nono-proxy/src/server.rs
#	crates/nono-proxy/src/tls_intercept/handle.rs
#	crates/nono-proxy/src/token.rs
kipz added a commit to kipz/nono that referenced this pull request Jul 24, 2026
Address nogent review feedback on nolabs-ai#1469:

- websocket.rs: read_response used unbounded read_line for both the
  status line and header lines, so an upstream that never sends a
  newline could grow the buffer without limit until OOM or the 30s
  timeout. Add a bounded line reader (8KB/line) that fails fast.

- h2_forward.rs: handle_h2_stream hardcoded websocket_path: None,
  skipping the upgrade_rules gate for RFC 8441 extended CONNECT
  streams. Detect :protocol: websocket and route it through the same
  check as the HTTP/1.1 Upgrade path, so enabling h2 WebSocket
  tunneling later can't silently bypass the allowlist.

Signed-off-by: James Carnegie <me@kipz.org>
@kipz
kipz force-pushed the kipz/redeem-phantoms-upstream branch from e9b75ba to 79658a9 Compare July 24, 2026 16:19
@lukehinds

Copy link
Copy Markdown
Contributor

@kipz , this one needs a bit of a rebase , a few network PRs landed before

@lukehinds lukehinds added this to the 0.70 milestone Jul 27, 2026
kipz added a commit to kipz/nono that referenced this pull request Aug 3, 2026
Address nogent review feedback on nolabs-ai#1469:

- websocket.rs: read_response used unbounded read_line for both the
  status line and header lines, so an upstream that never sends a
  newline could grow the buffer without limit until OOM or the 30s
  timeout. Add a bounded line reader (8KB/line) that fails fast.

- h2_forward.rs: handle_h2_stream hardcoded websocket_path: None,
  skipping the upgrade_rules gate for RFC 8441 extended CONNECT
  streams. Detect :protocol: websocket and route it through the same
  check as the HTTP/1.1 Upgrade path, so enabling h2 WebSocket
  tunneling later can't silently bypass the allowlist.

Signed-off-by: James Carnegie <me@kipz.org>
(cherry picked from commit 79658a9)
kipz added a commit to kipz/nono that referenced this pull request Aug 3, 2026
… PRs

Cross-PR semantic conflicts: rate_limit/redeem_phantoms/format/credential_name
test initializers, filter_child_env caller arg, and nolabs-ai#1443's CR/LF/NUL guard
re-applied on top of nolabs-ai#1469's rewritten resolve_nonce_in_header_value.

Signed-off-by: James Carnegie <me@kipz.org>
kipz added a commit to kipz/nono that referenced this pull request Aug 3, 2026
Address nogent review feedback on nolabs-ai#1469:

- websocket.rs: read_response used unbounded read_line for both the
  status line and header lines, so an upstream that never sends a
  newline could grow the buffer without limit until OOM or the 30s
  timeout. Add a bounded line reader (8KB/line) that fails fast.

- h2_forward.rs: handle_h2_stream hardcoded websocket_path: None,
  skipping the upgrade_rules gate for RFC 8441 extended CONNECT
  streams. Detect :protocol: websocket and route it through the same
  check as the HTTP/1.1 Upgrade path, so enabling h2 WebSocket
  tunneling later can't silently bypass the allowlist.

Signed-off-by: James Carnegie <me@kipz.org>
@kipz
kipz force-pushed the kipz/redeem-phantoms-upstream branch from 79658a9 to 9acede4 Compare August 3, 2026 14:38
kipz added a commit to kipz/nono that referenced this pull request Aug 3, 2026
Address nogent review feedback on nolabs-ai#1469:

- websocket.rs: read_response used unbounded read_line for both the
  status line and header lines, so an upstream that never sends a
  newline could grow the buffer without limit until OOM or the 30s
  timeout. Add a bounded line reader (8KB/line) that fails fast.

- h2_forward.rs: handle_h2_stream hardcoded websocket_path: None,
  skipping the upgrade_rules gate for RFC 8441 extended CONNECT
  streams. Detect :protocol: websocket and route it through the same
  check as the HTTP/1.1 Upgrade path, so enabling h2 WebSocket
  tunneling later can't silently bypass the allowlist.

Signed-off-by: James Carnegie <me@kipz.org>
@kipz
kipz force-pushed the kipz/redeem-phantoms-upstream branch from 9acede4 to 998f3e4 Compare August 3, 2026 21:44
kipz added a commit to kipz/nono that referenced this pull request Aug 3, 2026
Address nogent review feedback on nolabs-ai#1469:

- websocket.rs: read_response used unbounded read_line for both the
  status line and header lines, so an upstream that never sends a
  newline could grow the buffer without limit until OOM or the 30s
  timeout. Add a bounded line reader (8KB/line) that fails fast.

- h2_forward.rs: handle_h2_stream hardcoded websocket_path: None,
  skipping the upgrade_rules gate for RFC 8441 extended CONNECT
  streams. Detect :protocol: websocket and route it through the same
  check as the HTTP/1.1 Upgrade path, so enabling h2 WebSocket
  tunneling later can't silently bypass the allowlist.

Signed-off-by: James Carnegie <me@kipz.org>
kipz added a commit to kipz/nono that referenced this pull request Aug 6, 2026
Address nogent review feedback on nolabs-ai#1469:

- websocket.rs: read_response used unbounded read_line for both the
  status line and header lines, so an upstream that never sends a
  newline could grow the buffer without limit until OOM or the 30s
  timeout. Add a bounded line reader (8KB/line) that fails fast.

- h2_forward.rs: handle_h2_stream hardcoded websocket_path: None,
  skipping the upgrade_rules gate for RFC 8441 extended CONNECT
  streams. Detect :protocol: websocket and route it through the same
  check as the HTTP/1.1 Upgrade path, so enabling h2 WebSocket
  tunneling later can't silently bypass the allowlist.

Signed-off-by: James Carnegie <me@kipz.org>
@kipz
kipz force-pushed the kipz/redeem-phantoms-upstream branch from 50e0ac9 to 5fcef26 Compare August 6, 2026 10:31
kipz added a commit to kipz/nono that referenced this pull request Aug 9, 2026
Address nogent review feedback on nolabs-ai#1469:

- websocket.rs: read_response used unbounded read_line for both the
  status line and header lines, so an upstream that never sends a
  newline could grow the buffer without limit until OOM or the 30s
  timeout. Add a bounded line reader (8KB/line) that fails fast.

- h2_forward.rs: handle_h2_stream hardcoded websocket_path: None,
  skipping the upgrade_rules gate for RFC 8441 extended CONNECT
  streams. Detect :protocol: websocket and route it through the same
  check as the HTTP/1.1 Upgrade path, so enabling h2 WebSocket
  tunneling later can't silently bypass the allowlist.

Signed-off-by: James Carnegie <me@kipz.org>
@kipz
kipz force-pushed the kipz/redeem-phantoms-upstream branch from 5fcef26 to d3b11c5 Compare August 9, 2026 11:14
kipz added a commit to kipz/nono that referenced this pull request Aug 9, 2026
kipz added a commit to kipz/nono that referenced this pull request Aug 9, 2026
…n_header_value

Threads the matched route's redeem_phantoms through the absolute-form
forward-proxy path so nolabs-ai#1589 and nolabs-ai#1469 compose.

Signed-off-by: James Carnegie <me@kipz.org>
kipz added a commit to kipz/nono that referenced this pull request Aug 9, 2026
Resolutions:
- websocket.rs: kept nolabs-ai#1469's bounded line reads, added RFC 6455
  Sec-WebSocket-Accept validation from nolabs-ai#1443.
- handle.rs: adopted nolabs-ai#1443's run_websocket_tunnel split; kept the
  4-arg resolve_nonce_in_header_value (redeem_phantoms) from nolabs-ai#1469
  and nolabs-ai#1489's templated-phantom rewrite path.
- token.rs: CRLF/NUL fail-closed guard moved into rewrite_first_phantom
  so the grant-set/templated path is covered too.
- reverse.rs: header_pairs replaced by http1::parse_header_fields.
- Deduplicated RouteConfig 'upgrades' fields introduced twice by the merge.

Signed-off-by: James Carnegie <me@kipz.org>
kipz added a commit to kipz/nono that referenced this pull request Aug 9, 2026
…e nonce redemption

# Conflicts:
#	crates/nono-cli/src/proxy_runtime.rs
#	crates/nono-cli/src/tool-sandbox/token_broker.rs
#	crates/nono-proxy/src/config.rs
#	crates/nono-proxy/src/reverse.rs
#	crates/nono-proxy/src/route.rs
#	crates/nono-proxy/src/server.rs
#	crates/nono-proxy/src/tls_intercept/h2_forward.rs
#	crates/nono-proxy/src/tls_intercept/handle.rs
#	crates/nono-proxy/src/tls_intercept/websocket.rs
#	crates/nono-proxy/src/token.rs
@kipz
kipz force-pushed the kipz/redeem-phantoms-upstream branch from d3b11c5 to 66c72e6 Compare August 10, 2026 10:59
Routes may declare `redeem_phantoms`: broker credential names the route
will redeem from a caller-presented phantom, authorized by credential
name rather than by the consumer's grant set. The broker tracks the name
behind every issued phantom so a reissued historical value stays
redeemable, and a phantom for an unlisted credential fails closed.

Signed-off-by: James Carnegie <me@kipz.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Route-gated by-value resolution of caller-presented capture nonces

2 participants