Skip to content

feat(orchestration): let people read run nodes, triggers and ticks - #1332

Merged
kvlonge merged 12 commits into
mainfrom
feat/person-run-trigger-reads
Nov 7, 2026
Merged

kvlonge merged 12 commits into
mainfrom
feat/person-run-trigger-reads

Conversation

@kvlonge

@kvlonge kvlonge commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Why

The live UI shows a run's node outcomes, a target's triggers and each trigger's tick log. Until now only machine credentials could read these. People need the same reads, under the same rules as listing a target's runs: a refusal must not reveal whether an object exists.

Changes

  • New person endpoints: GET .../targets/{target}/runs/{run_id}/nodes, .../triggers and .../triggers/{trigger_id}/ticks.
  • Each one authorizes the listing first, inside one transaction, and only then checks that the run or trigger belongs to this location and target. Someone without view on the whole target gets the same node-free 403 for an existing, a cross-target or an unknown object. A 404 goes only to people who can view the whole target. Removed triggers stay readable.
  • Ticks default to 100 and are capped at 1000.
  • The machine reads now share the same in-transaction queries, with unchanged SQL. One store check covers run and trigger scope.
  • The TypeScript client is regenerated.
  • DuckShop: no edge case added, because this is an API read path with no project shape, data value or lifecycle sequence.

Verification

  • Denial cases compare forbidden with unknown, and they fail if the scope lookup is moved before authorization. A role matrix covers person sessions. 68 API, scheduler and run tests pass on SQLite and PostgreSQL.
  • Clippy, fensu, make check-ci. The TypeScript client check and tests pass.
  • Independent security review: pass with no findings. One accepted trade-off: older runs can list nodes that are no longer in the current snapshot, as the runs listing already does.

A refused location listing named the nodes the person could not view and
read differently for a location that does not exist. Listing refusals now
carry one fixed message with the decision ID, identical for unassigned and
unknown locations. The audit event keeps the missing nodes.
Every refusal from person authorization now shows fixed text per action
and its decision ID; the audit event keeps the missing nodes. Unknown
jobs and triggers are refused only after the caller is authorized for
the target, so an unknown object reads the same as a forbidden one.
Person sessions can read a run's nodes, the target's triggers and a
trigger's ticks. Each read authorizes like the runs list, with view on
every current node of the target, before looking the object up, so an
unknown run or trigger gets the same refusal as a forbidden one. A person
who may view the whole target gets 404 for an unknown object. The machine
reads share the same in-transaction queries.

Validated locally with SQLite and PostgreSQL: orchestration API tests,
including the forbidden-versus-unknown denial cases for the new reads
(a lookup-before-authorize mutation fails them) and the per-role session
matrix; clippy, fensu, and the regenerated TypeScript client check and
test. Not yet reviewed; stacked on the listing-denial branch.
One store read decides whether a person's target-scoped read may name a run
or a trigger. The handler and per-record read shapes join the existing
duplicate allowlists.
…er-reads

# Conflicts:
#	crates/sqlbuild-orchestration/src/authorization/main/mod.rs
#	crates/sqlbuild-orchestration/tests/api/test_denials.rs
@kvlonge
kvlonge enabled auto-merge (squash) November 6, 2026 18:40
…er-reads

# Conflicts:
#	crates/sqlbuild-orchestration/src/api/models.rs
#	crates/sqlbuild-orchestration/tests/api/test_denials.rs
@kvlonge
kvlonge merged commit fb13201 into main Nov 7, 2026
49 checks passed
@kvlonge
kvlonge deleted the feat/person-run-trigger-reads branch November 7, 2026 02:27
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