Skip to content

fix(repository): log each native broker refusal for operators - #3589

Merged
freeqaz merged 2 commits into
mainfrom
fix-2411
Oct 25, 2026
Merged

freeqaz merged 2 commits into
mainfrom
fix-2411

Conversation

@freeqaz

@freeqaz freeqaz commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator

Problem

On the native repository path, the repository broker refused pushes to another ref, requests for another repository, requests after the binding was withdrawn and requests over the per-session exchange limit without writing anything to the worker or the repository-credentials container log. Git shows the Agent only the HTTP status (for example a bare 503), so neither the Agent nor an operator could tell a withdrawal from a capacity refusal (finding 2411; the 4-request limit was documented in #3581). The OpenShell route already logs worker.openshell-git-denied with a reasonCode.

Change

  • transport/agent.ts: every refusal of a request that carried credentials emits one repository-credentials.denied record: status, reasonCode (the code the Agent received), reason (credential_refused, session_unavailable, capacity, request_refused, input_refused, not_dispatched, operation_conflict or service_error), operation (the route category, such as git-push, once known) and the session ID, Namespace and repositoryRef when the credential matches an open session. Unauthenticated requests are Git's normal challenge round trip and stay silent. Records never carry the bearer, a token, a URL, a ref or a body.
  • The session lookup for attribution (service.identify) matches only an open session's exact bearer digest. A request for a repository no binding names carries the session's Git credential but is refused before session lookup; the handler matches the presented token for log attribution only, so that record still names the session. Authentication itself is unchanged.
  • runService (the repository-credentials process composition) writes each record as one JSON line on stderr, next to its existing started/shutdown events.
  • No rate limit: one line per refused request, like the OpenShell route. Each request is its own TLS connection within the listener's socket limit, so volume is bounded by the Agent's own request rate; no internal retry loop produces refusals.
  • Docs: a "Read broker refusals" section in the prepared native Git reference and one sentence in the repository-credentials flow.

Not changed: the Agent still sees the same statuses. Git displays only the status code, and telling a withdrawal apart from an unavailable owner at the broker would need the worker-to-broker use protocol to carry the refusal reason, which is a contract change; raised separately.

Verification

  • postgres-prepared-repository-git-broker.test.mjs (real broker, real worker owner, PostgreSQL, real withdrawal through the HTTP API): each refusal logs exactly one record with the expected reason, reasonCode, operation and session identity: cold send (not_dispatched), mixed-ref push (input_refused), another repository / REST / GraphQL with the Git credential (credential_refused), REST with an API credential (request_refused), expired send window and the withdrawn fetch and discovery (not_dispatched). Successful clone, push, pull and an uncertain push log nothing. No record contains the bearer, the push ref or the repository name.
  • repository-credentials-transport-bounds.test.mjs: the per-session exchange limit logs capacity / exchange-capacity with the session ID.
  • repository-credentials-service-loss.test.mjs: the forked production runService process writes the JSON line on stderr for a credentialed refusal, nothing for the unauthenticated challenge, and never the bearer.
  • All three fail on main without the change. postgres-platform lane on a remote test VM: pass; local tsc --build, eslint and prettier clean.

Risks

Low. Logging only; answers, statuses and authorization are unchanged. A misbehaving Agent can raise log volume by sending many refused requests, at the same rate it could already send them.

Native repository broker refusals (a push to another ref, another
repository, a withdrawn binding, the per-session exchange limit) wrote
nothing to the worker or repository-credentials log, and Git shows the
Agent only the HTTP status (finding 2411).

The broker now writes one repository-credentials.denied JSON line per
refused request that carried a session credential: the code the Agent
received, where it was refused, the operation once known, and the
session ID, Namespace and repositoryRef. Unauthenticated challenge
round trips stay silent. No bearer, token, URL or ref is logged.
… session

A request for a repository that no binding names carries the session's
Git credential but is refused before session lookup. Match the token it
presents against open sessions (log attribution only) so its record
names the session, and test a Bearer REST request on the Git-only
route as request_refused.
@freeqaz
freeqaz requested a review from a team as a code owner October 25, 2026 06:04
@clawsweeper

clawsweeper Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review ended

ClawSweeper stopped reviewing this revision because the pull request closed.

View the workflow run.

@clawsweeper

clawsweeper Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

ClawSweeper status: review started.

I am starting a fresh review of this pull request: fix(repository): log each native broker refusal for operators This is item 1/1 in the current shard. Shard 0/1.

This temporary status tracks the active review worker. The completed review will appear in the durable ClawSweeper review comment.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Oct 25, 2026
@freeqaz
freeqaz merged commit d716121 into main Oct 25, 2026
39 checks passed
@freeqaz
freeqaz deleted the fix-2411 branch October 25, 2026 06:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant