Skip to content

feat(approval): close host gate contract gaps - #16

Merged
senamakel merged 39 commits into
epic/8369-security-migrationfrom
sec-649-tinysecurity-approval
Oct 25, 2026
Merged

senamakel merged 39 commits into
epic/8369-security-migrationfrom
sec-649-tinysecurity-approval

Conversation

@senamakel

@senamakel senamakel commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Closes #8
Part of tinyhumansai/openhuman#8369

Adds TinySecurity approval contract 1.30 and closes the approval gaps in #8. Unknown origins now fail closed like unlabelled origins, and require_approval prevents init-scope allowlists from shortcutting a human decision. Durable user-authorized tool and flow trust remain separate. Recent decisions can be listed across active scopes; rows carry a redacted session correlation; PurgeSession removes pending rows across scopes while preserving decided history. RecordAuditError2 stores only RedactMode::Text sanitized audit sink diagnostics, capped after redaction.

The engine already requires a verified VerifyHuman receipt before storing the ticket digest that PreauthorizeFlow2 checks; regression coverage preserves that boundary. Forced approval and detached/sub-agent presentation remain host-side: forced requests must be validated WebChat only and once-only, while detached card lifetime/classification stays with the host. Park deadline expiry is ExpireApproval2; CancelApproval2 is reserved for explicit cancellation.

Contract 1.30 is source-only for this epic. No checksum.toml or release artifact is updated; publication remains for #13.

Validation passed locally:

  • cargo fmt --all -- --check
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo build --all-targets --all-features
  • cargo test --all-features

The CI matrix must pass on Linux, macOS, and Windows before merge.

senamakel and others added 30 commits October 25, 2026 04:28
…pectation

Add assertions that an unknown origin is grant-only in the cron and
trusted-automation cases, and correct the operator expectation so it
requires both auto-approve and no required approval.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Added an Unknown variant to the Origin enum for cases where origin
classification fails or is unavailable, and routed it through the same
grant-only path as Unlabelled so it is treated as untrusted. The test
condition for operator origins was also corrected to match the intended
automatic-or-not-required logic.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a test asserting that a scope with require_approval set still routes a
whole-tool allowlisted call to Pending with no attribution, so the
per-scope override cannot be bypassed by a tool grant.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…ules

Calls with an unknown origin now follow the same whole-tool grant path as unlabelled calls, so they are no longer kept alive by a flow grant added later. Scopes that require approval also bypass the rule-based grant lookup entirely, and the affected test was reformatted.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add list_recent_decisions to read the newest durable decisions across all
registered scopes, excluding pending rows and bounding the result to 500.
Also restrict the require_approval scope bypass to the Approval rule so
other rules are still evaluated, and document the host responsibilities
for forced approval, sub-agent delegation, and deadline expiry.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Approval rows now carry the session id copied from the trusted caller
context so consumers can correlate a decision with the session that
requested it. The recent-decisions test was tightened to assert on
request ids, which verifies that pending rows are skipped and both
registered scopes are represented.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Approval rows now carry a session id, taken from the caller thread for
interactive requests and left unset for synthetic rows such as imported
history and trust-derived entries. The id is computed once before the row
is built so the same value is used throughout.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a purge_session port and engine method to remove undecided rows for one
exact session within a subject, plus a record_audit_error method that persists
only a redacted diagnostic after an audit sink failure without altering the
approval lifecycle. Binding validation now rejects rows whose session id does
not match the caller thread or falls outside the allowed length.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session id check now treats an absent session id as valid instead of
comparing it directly against the caller thread, which previously rejected
records that legitimately had no session id.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test fixtures for native grant receipts and approval rows now set the
newly added session_id and audit_error fields to None so they continue to
compile against the updated structs.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The scope check previously short-circuited to no approval whenever the
scope required approval and the rule was Approval, ignoring durable tool
and flow grants. It now only skips when neither durable grant applies, so
existing grants are respected. Tests set require_approval to false where
they exercise grant behaviour.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Rows now carry an optional session id that is checked against the caller
binding when loaded, and ListRecentDecisions2 reports terminal decisions
across registered scopes while omitting pending ones. PurgeSession removes
only pending rows for an exact scope/session pair, and RecordAuditError2
redacts, caps, and CAS-persists audit_error without changing approval state
or granting authority.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Remove PurgeApprovalSessionRequest2 from the redacted_debug! list since the type no longer exists, keeping the macro invocation compiling.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The scope replacement tests now assert that racing callers observe a pending approval rather than a denial, and the trusted reservation test grants the tool explicitly instead of relying on auto approval. Comments were updated to describe the configured allowlist and trusted grant behaviour.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The scope replacement concurrency test now registers the replacement with its own policy reference so the case where the incoming scope differs from the committed one is exercised.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Extend the scoped wire test to assert the three newly advertised method
constants and bump the expected contract version to 1.30. The
compatibility checks now confirm that 1.30 and 1.31 hosts are accepted
while 1.29 and older are rejected.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add LIST_RECENT_DECISIONS2, PURGE_APPROVAL_SESSION2 and RECORD_AUDIT_ERROR2 to the exported METHODS list so the new bus methods are discoverable alongside the existing approval and audit entries.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the wire test's expected CONTRACT_VERSION to match the newly released 1.30 contract so the assertion reflects the current protocol version.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The scope test now asserts that a required approval takes precedence over the registered tool allowlist, driving the request through a human decision before checking the approved state. This keeps the fixture aligned with the intended approval ordering.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session purge contract test now returns a Result and uses the question mark operator instead of unwrap, so serialization failures surface as test errors rather than panics.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replace the unchecked cast of the removed session count with a checked
conversion so an oversized length surfaces as an error instead of
silently truncating.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The native contract example now registers its approval scope with require_approval set to false, so the sample no longer demands an approval step that the surrounding grants do not provide.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The example now sets Origin::Unknown on the caller context and imports the
Origin type, keeping it compiling after the field was added to the struct.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The native contract example now builds its approval request with a Saas origin instead of Unknown, so the sample matches the origin the contract is meant to exercise.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reordered the imports in the approval scope native contract example so
they are sorted consistently. No behaviour change.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a test asserting that the debug output of a purge approval session
request redacts the raw session identity, matching the existing
redaction guarantees for other approval wire types.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Extend the approval callback tests with a purge_session stub on the fake
store and assertions covering successful purges, scope mismatches, and
unrelated receipts. Also assert that client construction rejects empty
scope lists and malformed audit stream ids, and that debug output redacts
client state.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Clone the scope and handle values used in the approval callback test
assertions so the originals remain available after the comparisons.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The runtime subject test now registers a store at the callback object path before constructing the approval clients, so the test exercises the same serving setup as the other cases.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
senamakel and others added 9 commits October 25, 2026 05:53
The purge-session assertions were moved out of the receipt validation test
into their own test so each case exercises a single behaviour. The new test
covers purging within the registered scope and rejecting a request whose
scope names a different user.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Move the empty-scope rejection check out of the broader store client test
into a dedicated test so the failure mode is isolated and easier to
diagnose.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add tests for idempotent audit error recording, rejection of conflicting
updates, and validation of empty diagnostics before a row is loaded. Also
assert that purging an empty session id is invalid and that the default
session purge port fails closed.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a test asserting that the session purge request and response debug
output redacts the session identifier, and extend the native contract
example to advertise and handle the purge session callback.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add an integration test that drives the approval engine through recording an
audit error, listing recent decisions and purging a session, asserting the
saved error and that the approved request appears in the recent list.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replace the fixture in the default_session_purge_port_fails_closed test with a minimal Ports implementation whose methods all return Unavailable, so the test exercises the trait's default purge_session behaviour rather than a fixture override.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…before audit error

The audit-error case now reserves and records execution on the approved
record before recording the audit error, so the error is attached to a
terminal row rather than a pending one. The redundant second evaluation
was dropped since the flow no longer needs it.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformatted a test signature, an import list, and an assertion to match rustfmt output. No behaviour changed.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Switch the callback name collection to iterate by reference so the
source list is no longer consumed, parsing each borrowed name instead.
This keeps the example's data available after building the names.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@senamakel
senamakel merged commit b8c6229 into epic/8369-security-migration Oct 25, 2026
14 of 16 checks passed
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