Skip to content

Slice 021: Permission gate + approval UI - #28

Merged
HackTuah merged 6 commits into
mainfrom
slice/021-permission-gate
Sep 20, 2026
Merged

HackTuah merged 6 commits into
mainfrom
slice/021-permission-gate

Conversation

@HackTuah

Copy link
Copy Markdown
Member

Slice 021 per slices/021-permission-gate/SLICE.md. Evidence in its PROOF.md; the canonicalisation measurement, findings and follow-ups in its NOTES.md.

  • Fingerprints over RFC 8785 (jcs ~> 0.2, the RFC vector as a test), the layered policy in docs/07's order, the Gate with expiries that survive restarts, the Session's approval_wait, the card, the header indicator and /permissions.
  • The tier is written once by the registry from the core modules' declared risks; every decision is decide_request/3.
  • Gate exit 0, 233 tests, coverage 72.45% (from 67.18%). Manual queue: AC2 and AC7, screenshots already in proof/.

… opens

ROADMAP row 021 to in_progress. NOTES.md carries the measurement of the
two RFC 8785 packages (rfc8785 1.0.0 refuses OTP 28; jcs 0.2.0 matches
the RFC's own vector byte for byte), the decision for jcs open to the
owner's veto, the nine-line build plan, the two-item manual queue, and
four deviations stated before any code. plan_check: PASS.

Signed-off-by: Ayla Croft <aylacroft@proton.me>
RFC 8785 canonical JSON for the approval fingerprints (docs/07), chosen
by the measurement in NOTES.md: jcs 0.2.0 matches the RFC's own vector
on this OTP; rfc8785 1.0.0 refuses OTP 28. VERSIONS row added; hex.audit
clean.

Signed-off-by: Ayla Croft <aylacroft@proton.me>
…te, approval_wait, the card and the audit

Lines 2 to 8 of the G1 plan. Trinity.Permissions.Fingerprint (sha256 over
RFC 8785 of tool, args, scope, cwd, canonicalization_version 1; the RFC's
vector is a test). The tool_permissions and approvals tables with their
schemas (Rule: the pattern language `*`, `key=glob`, `fp:<hex>`, `re:`;
Approval: status, decision, decided_at, decided_by, consumed_at,
expires_at) and Store. The tier's source: the registry hands the core
tools' declared risks to Permissions.put_core_tiers/1 at its start and
nothing else writes them. Policy.Layered in docs/07's order: session
grants, the newest unspent decision for the fingerprint (allow once and
deny are each consumed by one execution), persona settings, global rules,
the default by tier. Permissions.Gate: a request is a row then a
broadcast on approvals:<id> and approvals:all, an expiry timer, pending
rows reloaded at init; decide_request/3 records the decision and writes
the session grant or the global rule it implies. The runner turns an
:ask into a request and an approval_required result; the Session holds
those calls in approval_wait (012's empty state, now entered) and runs
them again when the last decision lands, where the policy re-derives the
fingerprint. TrinityWeb.ApprovalComponents (the card, the risk badge, the
pending indicator), the card in the chat, PermissionsLive at /permissions
(pending decidable, every decision with its time and decider, rules
revocable). scripts/dev_chat_on_test_registry.sh serves the chat on the
test registry for the screenshots, on its own database.

Tests, 25 in test/trinity/permissions and 4 in the LiveView suite: the
RFC vector, the pattern language, each policy layer and their order, the
Gate's rows and broadcasts, AC1 (a read tool never reaches the Gate),
AC2's two halves (approval_wait and the card; allow once runs the tool,
the final message follows), AC3 (a session grant: the identical call
runs, other arguments ask, another session asks), AC4 (an always rule
with a glob, outside it asks, revocable), AC5 (a denial the model reads,
the turn goes on), AC6 (expiry into a denial decided by expiry within
the timeout), AC7 (every decision a row with decided_at; the audit page),
AC8 (killed in approval_wait: the row pending and decidable, the
restarted session idle). Screenshots in proof/. Gate exit 0, 233 tests;
credo --strict --all clean; sobelow clean; docs/01, 05 and 07 synced.

Signed-off-by: Ayla Croft <aylacroft@proton.me>
PROOF.md with every criterion's evidence: AC1, AC3, AC4, AC5, AC6 and
AC8 from the Session-flow and Gate tests, AC2 and AC7 from their test
halves plus screenshots under proof/ taken from the chat on the test
registry, with the owner's two-step manual queue. NOTES.md carries the
eight findings and the follow-ups. coverage.tsv: 021 at 72.45% from
67.18%. ROADMAP row 021 to done. Gate exit 0, 233 tests.

Signed-off-by: Ayla Croft <aylacroft@proton.me>
Signed-off-by: Ayla Croft <aylacroft@proton.me>
…a missing table

Red: the postgres job on pull request #28 (run 35528824468) booted the
application before migrating and the Gate's init/1 read the approvals
table: `relation "approvals" does not exist`, the application refused to
start. The reload is a handle_continue that rescues into a warning, so a
Gate that cannot read its table starts empty and the chat starts; a
request made before the table exists fails on its own insert. NOTES.md
finding 9, PROOF.md correction.

Signed-off-by: Ayla Croft <aylacroft@proton.me>
@HackTuah
HackTuah merged commit d99ea10 into main Sep 20, 2026
4 checks passed
@HackTuah
HackTuah deleted the slice/021-permission-gate branch September 20, 2026 18:36
@HackTuah HackTuah mentioned this pull request Sep 20, 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