Skip to content

Converge main: telemetry auth wiring + absorb 202 reconcile - #367

Closed
aliasfoxkde wants to merge 27 commits into
mainfrom
chore/converge-main-20261103
Closed

aliasfoxkde wants to merge 27 commits into
mainfrom
chore/converge-main-20261103

Conversation

@aliasfoxkde

Copy link
Copy Markdown
Owner

Summary

  • Mounts /metrics and /dashboard behind the auth middleware by default; public only with GITFORGE_PUBLIC_TELEMETRY=1 (F5 route-boundary wiring)
  • Absorbs co-tenant reconcile commits from the 202 instance main
  • Full GitForge chain green on f1ebec0 (run df5fc6ec); merge SHAs compiled clean locally

Test plan

  • Telemetry integration tests pin anonymous 401 default / 200 opt-in
  • GitForge CI pipeline green on f1ebec0
  • Post-merge GitForge run green on merge SHA d74a1c7

aliasfoxkde and others added 27 commits October 30, 2026 10:56
The deployed binary on 192.168.1.202 (69245e2 bundle, Oct 29) predates
the job-level environment field; its parser rejects the key (fail-closed
per 0857218), so every push event for 7f4e8fd failed with 'unknown job
key environment'. The top-level environment: {} remains — that one the
deployed parser requires and accepts.
Clippy (rust 1.99 in dsc-ci-rust:12) flags the manual str::repeat
pattern as manual_str_repeat/manual_repeat_n under -D warnings.

Co-Authored-By: Claude <noreply@anthropic.com>
A busy branch occupied its repository's FIFO trigger lane for the
whole of each ~10-minute pipeline, leaving other refs of the same
repo starved: five main-branch trigger requests sat pending for
hours while codex-branch pushes kept re-arming the lane, and the
republish redrive could not help because it re-queued into the
same lane. Lane keys now include the ref name, so ordering holds
within a ref while cross-ref starvation is impossible.
Route-by-route inventory of the API surface with auth class and
per-handler authorization check for every registered route. Anchors the
registration and webhook findings fixed in the preceding commit.
The skip_if evaluator concatenated an `export KEY=value` line built
from the detect-changes job receipt into a single bash -c script. A
receipt-controlled value such as `1; touch /tmp/pwned` therefore
executed as shell code inside the CI service. The environment is now
delivered through Command::envs — never interpolated into the script
text — and CI_CHANGE_FILE_COUNT defaults to 0 when the receipt omits
it. Regression tests cover both properties.

Refs #358
GET /api/runners and GET /api/runners/{id} let any authenticated
user enumerate the fleet (names, capacity, heartbeats) — recon
directly feeding the F1-style identity spoofing. Apply the same
can_manage_jobs role gate retirement already uses, and fix the
integration tests that minted admin JWTs without persisting the
role row the middleware re-resolves.

Co-Authored-By: Claude <noreply@anthropic.com>
Authz audit F3: GET /api/runners and /api/runners/{id} accepted any
authenticated user, exposing fleet inventory (names, capacity,
heartbeat) to plain developers. Both reads now require maintainer or
admin via can_manage_jobs, matching the write-side gate.

Also records the 2026-11-01 authz audit, partial security audit, and
the 2026-11-02 production validation campaign (G1 pipeline-id drop in
trigger delegation, G2 uploaded-config bypass, G3 missing CI /metrics).
POST /api/pipelines/{id}/runs dropped the pipeline id when delegating
to the CI trigger, so the run bound to the repo's active pipeline and
the trigger dedup key — (repo_id, new_hash) — made two pipelines on
one commit dedup onto the first's row. The API-uploaded config was
bypassed whenever the pushed revision carried no committed
.gitforge.yml: the consumer fell back to cache/persisted/default
(G1/G2, docs/planning/PRODUCTION_VALIDATION_20261102.md).

The trigger request now carries an explicit pipeline_id, the dedup
key includes it (NULL keeps the old push-trigger semantics for
existing rows, added by a guarded column migration), and the webhook
event serializer forwards it. The old-hash sentinel uses git's fixed
all-zero ref-deletion value via is_zero_hash instead of a hard-coded
40-zero string.

Reproduced live on 202 (runs e80ed74f, 01f104df executed the wrong
pipeline); this change makes the requested definition authoritative.
Covers G1: two explicit pipeline triggers on one commit must each get
their own trigger request; keying on (repo, hash) alone made the
second dedup onto the first's row and never run.
Queued runs for a (repo, ref, pipeline) are superseded when a newer
push lands, cancelling never-dispatched jobs so runner capacity
serves the tip. Includes image-registry allowlist validation and
runner fleet-pause runbook guidance.
The runners_online/pipelines_total/jobs_total/repos_total/artifacts_total
gauges had no production setter and stayed at zero while real work flowed
through the CI service. A 30s sweep now reads the dashboard counts plus a
new JobQueries::count_all into the gauges.

Co-Authored-By: Claude <noreply@anthropic.com>
…dicts

- /metrics and /dashboard require auth unless GITFORGE_PUBLIC_TELEMETRY=1
  (F5); /health stays public; decision core + middleware tests
- pipeline_runs.ref_name column; webhook/ci populate it; supersede and
  integration/scheduler tests updated
- release gate: unconditional-job check accepts satisfied rows
  (succeeded or skipped); quality-chain names stay fails-closed; gate
  tests extended
- ledger verdicts for F1-F5 and the 2026-11-02 queue incident; F4
  recorded as a design decision; architecture audit appended to the
  execution plan; __pycache__ ignored

Co-Authored-By: Claude <noreply@anthropic.com>
Fix middleware-branch test expectations: /dashboard has its own route
merge and never hits the PUBLIC_PATHS skip branch; " 1 " is a valid
trim-tolerant opt-in.
/metrics and /dashboard were merged into public_routes, bypassing the
auth middleware entirely, so the GITFORGE_PUBLIC_TELEMETRY gate was dead
code at the route boundary. Wire both surfaces into the protected router
by default and into public_routes only on explicit opt-in. Integration
tests now pin the boundary: anonymous 401 by default, 200 with opt-in.
Make telemetry_is_public crate-visible and clone the metrics router so
it can be merged into either the public or protected router without a
double move.
The default-posture telemetry routes were merged into the /api-nested
protected router, which relocated them to /api/metrics and /api/dashboard
and left the canonical paths a 404. Mount them at top level behind their
own auth-middleware layer instead. Serialize the three env-mutating
telemetry tests with a shared mutex: the decision is read from the
process env at router-build time, so parallel test threads race and
randomly invert the asserted posture.
/metrics and /dashboard are now mounted at top level behind the auth
middleware unless GITFORGE_PUBLIC_TELEMETRY=1. Full GitForge chain
green on f1ebec0 (run df5fc6ec).
# Conflicts:
#	crates/gitforge-api/src/middleware.rs
@coderabbitai

coderabbitai Bot commented Nov 3, 2026

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 06d81314-2b99-40cb-b648-2e67b9b85f25
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

🤖 AI Code Review Results

AI review skipped: provider 'anthropic' is not configured; deterministic gates remain authoritative.

✅ No critical issues found.

AI Code Review powered by GitForge

@aliasfoxkde

Copy link
Copy Markdown
Owner Author

Closing as duplicate: this branch's tree hash (914820ed) is byte-identical to PR #368 (reconcile/linearize), which carries the same content as a linearized history — required because the Safeguards ruleset enforces required_linear_history with no bypass actors. If #368's shape doesn't work, reopen this one; the content is identical.

@aliasfoxkde aliasfoxkde closed this Nov 3, 2026
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