diff --git a/skills/access-request-review/SKILL.md b/skills/access-request-review/SKILL.md new file mode 100644 index 00000000..cd670310 --- /dev/null +++ b/skills/access-request-review/SKILL.md @@ -0,0 +1,71 @@ +--- +name: access-request-review +description: Review a bounded access request against policy and emit a least-privilege grant proposal or denial. +source: + type: cli-tool + command: node + args: + - run.mjs + input_mode: stdin + cwd: . + timeout_seconds: 30 +inputs: + access_request: + type: json + required: true + description: Request packet with requester, requested resource, requested action, business justification, and optional ticket metadata. + policy: + type: json + required: true + description: Access policy with allowed roles, resources, actions, TTL caps, approval rules, and escalation rules. + current_entitlements: + type: json + required: true + description: Current role and grant state for the requester. + objective: + type: string + required: false + description: Optional operator intent for the review. +runx: + category: security + input_resolution: + required: + - access_request + - policy + - current_entitlements +--- + +# access-request-review + +Use this skill when an operator needs a bounded access decision before a +human-approved one-time grant. The skill compares a request, the governing +policy, and current entitlements, then returns `grant`, `deny`, or +`needs_human_review`. + +The skill never creates access, calls identity providers, sends approval +messages, stores credentials, or widens authority outside the supplied policy. +When access is allowed it emits a least-privilege grant proposal with a bounded +TTL, exact scope, approval gate, escalation lane, and evidence citations. + +## Inputs + +- `access_request`: requester id, role, action, resource, requested scope, + justification, ticket id, and optional requested TTL. +- `policy`: allowed roles, resources, actions, maximum TTL, denied resources, + sensitive resources, required approvals, and break-glass rules. +- `current_entitlements`: current grants and group/role state for the requester. +- `objective`: optional operator intent. + +## Output + +The runner returns JSON with: + +- `decision_packet` object: typed decision packet. +- `grant_proposal` object: one-time proposal when the decision is `grant`. +- `escalation` object: `required`, `lane`, `reason`, and optional `ticket_id` + for human approval or denial escalation. +- `evidence_json` object: compact review evidence for external verification. +- `report` string: human-readable review summary. + +Decisions are deterministic and fail closed when request, policy, or entitlement +facts are missing. diff --git a/skills/access-request-review/X.yaml b/skills/access-request-review/X.yaml new file mode 100644 index 00000000..ae50dad3 --- /dev/null +++ b/skills/access-request-review/X.yaml @@ -0,0 +1,182 @@ +skill: access-request-review +version: "0.1.0" + +catalog: + kind: skill + audience: public + visibility: public + role: canonical + +policy: + side_effects: none + grant_mutation: denied + network_during_run: denied + secrets_required: false + filesystem: + read: [] + write: [] + +harness: + cases: + - name: least-privilege-grant-proposal + runner: default + inputs: + objective: "Review a temporary production log access request for incident response." + access_request: + request_id: req-2026-06-23-001 + requester: + id: user-17 + role: oncall_engineer + team: payments + action: read + resource: prod/payments/logs/service-a + requested_scope: logs.read:prod/payments/service-a/* + justification: "Investigate incident INC-2042 elevated payment retries." + ticket_id: INC-2042 + requested_ttl_minutes: 120 + policy: + policy_id: access-policy-demo-v1 + max_ttl_minutes: 240 + allowed_roles: + oncall_engineer: + actions: [read] + resources: + - prod/payments/logs/* + scope_prefixes: + - logs.read:prod/payments/ + denied_resources: + - prod/payments/secrets + sensitive_resources: + - prod/payments/logs/* + required_approvals: + sensitive_resource: human_approval + grant_defaults: + approval_gate: human_approval_required + current_entitlements: + subject_id: user-17 + roles: [oncall_engineer] + current_grants: + - grant_id: grant-staging-logs + scope: logs.read:staging/payments/* + expires_at: "2026-06-23T12:00:00Z" + expect: + status: sealed + receipt: + schema: runx.receipt.v1 + state: sealed + disposition: closed + reason_code: process_closed + + - name: deny-for-disallowed-resource + runner: default + inputs: + objective: "Reject direct secret access." + access_request: + request_id: req-2026-06-23-002 + requester: + id: user-21 + role: oncall_engineer + team: payments + action: read + resource: prod/payments/secrets + requested_scope: secrets.read:prod/payments/* + justification: "Need to check API keys." + ticket_id: INC-2043 + requested_ttl_minutes: 60 + policy: + policy_id: access-policy-demo-v1 + max_ttl_minutes: 240 + allowed_roles: + oncall_engineer: + actions: [read] + resources: + - prod/payments/logs/* + scope_prefixes: + - logs.read:prod/payments/ + denied_resources: + - prod/payments/secrets + sensitive_resources: + - prod/payments/logs/* + required_approvals: + sensitive_resource: human_approval + grant_defaults: + approval_gate: human_approval_required + current_entitlements: + subject_id: user-21 + roles: [oncall_engineer] + current_grants: [] + expect: + status: sealed + receipt: + schema: runx.receipt.v1 + state: sealed + disposition: closed + reason_code: process_closed + + - name: missing-justification-fails-closed + runner: default + inputs: + objective: "Reject incomplete access requests." + access_request: + request_id: req-2026-06-23-003 + requester: + id: user-22 + role: support_agent + action: read + resource: prod/support/tickets + requested_scope: tickets.read:prod/support/* + requested_ttl_minutes: 30 + policy: + policy_id: access-policy-demo-v1 + max_ttl_minutes: 60 + allowed_roles: + support_agent: + actions: [read] + resources: + - prod/support/tickets + scope_prefixes: + - tickets.read:prod/support/ + denied_resources: [] + sensitive_resources: [] + required_approvals: {} + current_entitlements: + subject_id: user-22 + roles: [support_agent] + current_grants: [] + expect: + status: failure + +runners: + default: + default: true + type: cli-tool + command: node + input_mode: stdin + args: + - run.mjs + outputs: + decision_packet: object + grant_proposal: object + escalation: object + evidence_json: object + report: string + artifacts: + wrap_as: access_request_review_packet + packet: runx.security.access_request_review.v1 + inputs: + access_request: + type: json + required: true + description: Bounded access request packet. + policy: + type: json + required: true + description: Governing access policy. + current_entitlements: + type: json + required: true + description: Current role and grant state for the requester. + objective: + type: string + required: false + description: Operator intent for the review. diff --git a/skills/access-request-review/evidence/clean-install.json b/skills/access-request-review/evidence/clean-install.json new file mode 100644 index 00000000..8683db5b --- /dev/null +++ b/skills/access-request-review/evidence/clean-install.json @@ -0,0 +1,43 @@ +{ + "status": "success", + "registry": { + "action": "install", + "source": "remote", + "ref": "lubuseb/access-request-review@sha-2100c1996336", + "install": { + "status": "installed", + "destination": "/tmp/runx-access-request-review-clean-install/lubuseb/access-request-review/sha-2100c1996336/SKILL.md", + "skill_name": "access-request-review", + "source": "runx-registry", + "source_label": "runx registry", + "skill_id": "lubuseb/access-request-review", + "version": "sha-2100c1996336", + "digest": "sha256:5d7e4a0c2122f7f98d418827f0115f81c158004ac9a12f2236ba6eb212d60aee", + "profile_digest": "sha256:865d7c9c5be962b752080face77831ddbf4d110b6fae85ee40baba3dae6b436e", + "profile_state_path": "/tmp/runx-access-request-review-clean-install/lubuseb/access-request-review/sha-2100c1996336/.runx/profile.json", + "runner_names": [ + "default" + ], + "trust_tier": "community" + }, + "receipt_metadata": { + "destination": "/tmp/runx-access-request-review-clean-install/lubuseb/access-request-review/sha-2100c1996336/SKILL.md", + "digest": "sha256:5d7e4a0c2122f7f98d418827f0115f81c158004ac9a12f2236ba6eb212d60aee", + "install_count": 1, + "package_digest": "4dadabe78e9b528dd7b256eaf9bbefb7a272e4c0e1edc401ec6de2f9758d858a", + "profile_digest": "sha256:865d7c9c5be962b752080face77831ddbf4d110b6fae85ee40baba3dae6b436e", + "publisher": { + "display_name": "LubuSeb", + "handle": "lubuseb", + "id": "user_53f00ae7ec2363e37ac6ff68", + "kind": "user" + }, + "ref": "lubuseb/access-request-review@sha-2100c1996336", + "skill_id": "lubuseb/access-request-review", + "source_label": "runx registry", + "status": "installed", + "trust_tier": "community", + "version": "sha-2100c1996336" + } + } +} diff --git a/skills/access-request-review/evidence/clean-install.stderr.txt b/skills/access-request-review/evidence/clean-install.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/skills/access-request-review/evidence/dogfood-output.json b/skills/access-request-review/evidence/dogfood-output.json new file mode 100644 index 00000000..d3600097 --- /dev/null +++ b/skills/access-request-review/evidence/dogfood-output.json @@ -0,0 +1,465 @@ +{ + "closure": { + "closed_at": "2026-06-23T05:01:03.689Z", + "disposition": "closed", + "reason_code": "process_closed", + "summary": "cli-tool default completed" + }, + "execution": { + "exit_code": 0, + "skill_claim": { + "decision_packet": { + "action": "read", + "approval_gate": "human_approval_required", + "decision": "grant", + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "evidence_refs": [ + "request:req-2026-06-23-001", + "policy:access-policy-demo-v1", + "entitlements:user-17" + ], + "least_privilege_scope": "logs.read:prod/payments/service-a", + "objective": "Dogfood access request review for Frantic bounty 55.", + "policy_id": "access-policy-demo-v1", + "reasons": [ + "request matches allowed role, action, resource, and scope prefix", + "ttl bounded to 120 minutes by policy max 240", + "proposal is gated; no access is issued by this skill" + ], + "request_id": "req-2026-06-23-001", + "requested_scope": "logs.read:prod/payments/service-a/*", + "resource": "prod/payments/logs/service-a", + "safeguards": { + "executes_grant": false, + "mutates_grants": false, + "read_only": true, + "requires_human_approval": true + }, + "schema": "runx.security.access_request_review.v1", + "subject_id": "user-17", + "ttl_minutes": 120 + }, + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "evidence_json": { + "artifact": "access-request-review", + "observations": { + "action": "read", + "approval_gate": "human_approval_required", + "current_grant_count": 1, + "decision": "grant", + "entitlement_digest": "sha256:39f793146c7d0523fe0a0267640093d3a124f3efa49ce2bd128075a7056fc4e7", + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "escalation_path": "human_approval_required", + "evidence_refs": [ + "request:req-2026-06-23-001", + "policy:access-policy-demo-v1", + "entitlements:user-17" + ], + "least_privilege_scope": "logs.read:prod/payments/service-a", + "policy_digest": "sha256:fed71a99fc94be00827e5a8fc8678ee5d07b3ff9fb2830bc3b718cff29bb8a9c", + "policy_id": "access-policy-demo-v1", + "reasons": [ + "request matches allowed role, action, resource, and scope prefix", + "ttl bounded to 120 minutes by policy max 240", + "proposal is gated; no access is issued by this skill" + ], + "request_digest": "sha256:42e9ee73c27845ed1fea00efce5f1d509b9bb97322d8c891ed74a78a14681d5a", + "request_id": "req-2026-06-23-001", + "requested_scope": "logs.read:prod/payments/service-a/*", + "requester_roles": [ + "oncall_engineer" + ], + "resource": "prod/payments/logs/service-a", + "subject_id": "user-17", + "ttl_minutes": 120 + }, + "schema": "frantic.delivery.evidence.v1" + }, + "grant_proposal": { + "action": "read", + "approval_gate": "human_approval_required", + "execution_status": "proposal_only", + "handoff": { + "catalog_skill": "least-privilege-auditor", + "requires_human_approval": true + }, + "issued_by_skill": false, + "justification": "Investigate incident INC-2042 elevated payment retries.", + "policy_id": "access-policy-demo-v1", + "proposal_id": "grant-proposal-req-2026-06-23-001", + "resource": "prod/payments/logs/service-a", + "schema": "runx.security.one_time_grant_proposal.v1", + "scope": "logs.read:prod/payments/service-a", + "subject_id": "user-17", + "ticket_id": "INC-2042", + "ttl_minutes": 120 + }, + "report": "# access-request-review report\n\nRequest: req-2026-06-23-001\nSubject: user-17\nPolicy: access-policy-demo-v1\nDecision: grant\nResource: prod/payments/logs/service-a\nRequested scope: logs.read:prod/payments/service-a/*\nLeast-privilege scope: logs.read:prod/payments/service-a\nTTL: 120 minutes\nApproval gate: human_approval_required\nEscalation: human_approval_required\n\n## Reasons\n- request matches allowed role, action, resource, and scope prefix\n- ttl bounded to 120 minutes by policy max 240\n- proposal is gated; no access is issued by this skill\n\nGrant proposal grant-proposal-req-2026-06-23-001 is proposal-only and requires human_approval_required.\nThis skill does not mutate grants, move secrets, or call identity-provider APIs." + }, + "stderr": "", + "stdout": "{\n \"decision_packet\": {\n \"schema\": \"runx.security.access_request_review.v1\",\n \"decision\": \"grant\",\n \"request_id\": \"req-2026-06-23-001\",\n \"subject_id\": \"user-17\",\n \"objective\": \"Dogfood access request review for Frantic bounty 55.\",\n \"policy_id\": \"access-policy-demo-v1\",\n \"resource\": \"prod/payments/logs/service-a\",\n \"action\": \"read\",\n \"requested_scope\": \"logs.read:prod/payments/service-a/*\",\n \"least_privilege_scope\": \"logs.read:prod/payments/service-a\",\n \"ttl_minutes\": 120,\n \"approval_gate\": \"human_approval_required\",\n \"escalation\": {\n \"required\": true,\n \"lane\": \"human_approval_required\",\n \"reason\": \"one-time grant proposal requires human approval before any access is issued\",\n \"ticket_id\": \"INC-2042\"\n },\n \"reasons\": [\n \"request matches allowed role, action, resource, and scope prefix\",\n \"ttl bounded to 120 minutes by policy max 240\",\n \"proposal is gated; no access is issued by this skill\"\n ],\n \"evidence_refs\": [\n \"request:req-2026-06-23-001\",\n \"policy:access-policy-demo-v1\",\n \"entitlements:user-17\"\n ],\n \"safeguards\": {\n \"read_only\": true,\n \"mutates_grants\": false,\n \"executes_grant\": false,\n \"requires_human_approval\": true\n }\n },\n \"grant_proposal\": {\n \"schema\": \"runx.security.one_time_grant_proposal.v1\",\n \"proposal_id\": \"grant-proposal-req-2026-06-23-001\",\n \"subject_id\": \"user-17\",\n \"action\": \"read\",\n \"resource\": \"prod/payments/logs/service-a\",\n \"scope\": \"logs.read:prod/payments/service-a\",\n \"ttl_minutes\": 120,\n \"approval_gate\": \"human_approval_required\",\n \"ticket_id\": \"INC-2042\",\n \"justification\": \"Investigate incident INC-2042 elevated payment retries.\",\n \"policy_id\": \"access-policy-demo-v1\",\n \"issued_by_skill\": false,\n \"execution_status\": \"proposal_only\",\n \"handoff\": {\n \"catalog_skill\": \"least-privilege-auditor\",\n \"requires_human_approval\": true\n }\n },\n \"escalation\": {\n \"required\": true,\n \"lane\": \"human_approval_required\",\n \"reason\": \"one-time grant proposal requires human approval before any access is issued\",\n \"ticket_id\": \"INC-2042\"\n },\n \"evidence_json\": {\n \"schema\": \"frantic.delivery.evidence.v1\",\n \"artifact\": \"access-request-review\",\n \"observations\": {\n \"request_id\": \"req-2026-06-23-001\",\n \"policy_id\": \"access-policy-demo-v1\",\n \"policy_digest\": \"sha256:fed71a99fc94be00827e5a8fc8678ee5d07b3ff9fb2830bc3b718cff29bb8a9c\",\n \"request_digest\": \"sha256:42e9ee73c27845ed1fea00efce5f1d509b9bb97322d8c891ed74a78a14681d5a\",\n \"entitlement_digest\": \"sha256:39f793146c7d0523fe0a0267640093d3a124f3efa49ce2bd128075a7056fc4e7\",\n \"subject_id\": \"user-17\",\n \"requester_roles\": [\n \"oncall_engineer\"\n ],\n \"action\": \"read\",\n \"resource\": \"prod/payments/logs/service-a\",\n \"requested_scope\": \"logs.read:prod/payments/service-a/*\",\n \"decision\": \"grant\",\n \"least_privilege_scope\": \"logs.read:prod/payments/service-a\",\n \"ttl_minutes\": 120,\n \"approval_gate\": \"human_approval_required\",\n \"escalation\": {\n \"required\": true,\n \"lane\": \"human_approval_required\",\n \"reason\": \"one-time grant proposal requires human approval before any access is issued\",\n \"ticket_id\": \"INC-2042\"\n },\n \"escalation_path\": \"human_approval_required\",\n \"current_grant_count\": 1,\n \"reasons\": [\n \"request matches allowed role, action, resource, and scope prefix\",\n \"ttl bounded to 120 minutes by policy max 240\",\n \"proposal is gated; no access is issued by this skill\"\n ],\n \"evidence_refs\": [\n \"request:req-2026-06-23-001\",\n \"policy:access-policy-demo-v1\",\n \"entitlements:user-17\"\n ]\n }\n },\n \"report\": \"# access-request-review report\\n\\nRequest: req-2026-06-23-001\\nSubject: user-17\\nPolicy: access-policy-demo-v1\\nDecision: grant\\nResource: prod/payments/logs/service-a\\nRequested scope: logs.read:prod/payments/service-a/*\\nLeast-privilege scope: logs.read:prod/payments/service-a\\nTTL: 120 minutes\\nApproval gate: human_approval_required\\nEscalation: human_approval_required\\n\\n## Reasons\\n- request matches allowed role, action, resource, and scope prefix\\n- ttl bounded to 120 minutes by policy max 240\\n- proposal is gated; no access is issued by this skill\\n\\nGrant proposal grant-proposal-req-2026-06-23-001 is proposal-only and requires human_approval_required.\\nThis skill does not mutate grants, move secrets, or call identity-provider APIs.\"\n}\n", + "structured_output": { + "decision_packet": { + "action": "read", + "approval_gate": "human_approval_required", + "decision": "grant", + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "evidence_refs": [ + "request:req-2026-06-23-001", + "policy:access-policy-demo-v1", + "entitlements:user-17" + ], + "least_privilege_scope": "logs.read:prod/payments/service-a", + "objective": "Dogfood access request review for Frantic bounty 55.", + "policy_id": "access-policy-demo-v1", + "reasons": [ + "request matches allowed role, action, resource, and scope prefix", + "ttl bounded to 120 minutes by policy max 240", + "proposal is gated; no access is issued by this skill" + ], + "request_id": "req-2026-06-23-001", + "requested_scope": "logs.read:prod/payments/service-a/*", + "resource": "prod/payments/logs/service-a", + "safeguards": { + "executes_grant": false, + "mutates_grants": false, + "read_only": true, + "requires_human_approval": true + }, + "schema": "runx.security.access_request_review.v1", + "subject_id": "user-17", + "ttl_minutes": 120 + }, + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "evidence_json": { + "artifact": "access-request-review", + "observations": { + "action": "read", + "approval_gate": "human_approval_required", + "current_grant_count": 1, + "decision": "grant", + "entitlement_digest": "sha256:39f793146c7d0523fe0a0267640093d3a124f3efa49ce2bd128075a7056fc4e7", + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "escalation_path": "human_approval_required", + "evidence_refs": [ + "request:req-2026-06-23-001", + "policy:access-policy-demo-v1", + "entitlements:user-17" + ], + "least_privilege_scope": "logs.read:prod/payments/service-a", + "policy_digest": "sha256:fed71a99fc94be00827e5a8fc8678ee5d07b3ff9fb2830bc3b718cff29bb8a9c", + "policy_id": "access-policy-demo-v1", + "reasons": [ + "request matches allowed role, action, resource, and scope prefix", + "ttl bounded to 120 minutes by policy max 240", + "proposal is gated; no access is issued by this skill" + ], + "request_digest": "sha256:42e9ee73c27845ed1fea00efce5f1d509b9bb97322d8c891ed74a78a14681d5a", + "request_id": "req-2026-06-23-001", + "requested_scope": "logs.read:prod/payments/service-a/*", + "requester_roles": [ + "oncall_engineer" + ], + "resource": "prod/payments/logs/service-a", + "subject_id": "user-17", + "ttl_minutes": 120 + }, + "schema": "frantic.delivery.evidence.v1" + }, + "grant_proposal": { + "action": "read", + "approval_gate": "human_approval_required", + "execution_status": "proposal_only", + "handoff": { + "catalog_skill": "least-privilege-auditor", + "requires_human_approval": true + }, + "issued_by_skill": false, + "justification": "Investigate incident INC-2042 elevated payment retries.", + "policy_id": "access-policy-demo-v1", + "proposal_id": "grant-proposal-req-2026-06-23-001", + "resource": "prod/payments/logs/service-a", + "schema": "runx.security.one_time_grant_proposal.v1", + "scope": "logs.read:prod/payments/service-a", + "subject_id": "user-17", + "ticket_id": "INC-2042", + "ttl_minutes": 120 + }, + "report": "# access-request-review report\n\nRequest: req-2026-06-23-001\nSubject: user-17\nPolicy: access-policy-demo-v1\nDecision: grant\nResource: prod/payments/logs/service-a\nRequested scope: logs.read:prod/payments/service-a/*\nLeast-privilege scope: logs.read:prod/payments/service-a\nTTL: 120 minutes\nApproval gate: human_approval_required\nEscalation: human_approval_required\n\n## Reasons\n- request matches allowed role, action, resource, and scope prefix\n- ttl bounded to 120 minutes by policy max 240\n- proposal is gated; no access is issued by this skill\n\nGrant proposal grant-proposal-req-2026-06-23-001 is proposal-only and requires human_approval_required.\nThis skill does not mutate grants, move secrets, or call identity-provider APIs." + } + }, + "payload": { + "decision_packet": { + "action": "read", + "approval_gate": "human_approval_required", + "decision": "grant", + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "evidence_refs": [ + "request:req-2026-06-23-001", + "policy:access-policy-demo-v1", + "entitlements:user-17" + ], + "least_privilege_scope": "logs.read:prod/payments/service-a", + "objective": "Dogfood access request review for Frantic bounty 55.", + "policy_id": "access-policy-demo-v1", + "reasons": [ + "request matches allowed role, action, resource, and scope prefix", + "ttl bounded to 120 minutes by policy max 240", + "proposal is gated; no access is issued by this skill" + ], + "request_id": "req-2026-06-23-001", + "requested_scope": "logs.read:prod/payments/service-a/*", + "resource": "prod/payments/logs/service-a", + "safeguards": { + "executes_grant": false, + "mutates_grants": false, + "read_only": true, + "requires_human_approval": true + }, + "schema": "runx.security.access_request_review.v1", + "subject_id": "user-17", + "ttl_minutes": 120 + }, + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "evidence_json": { + "artifact": "access-request-review", + "observations": { + "action": "read", + "approval_gate": "human_approval_required", + "current_grant_count": 1, + "decision": "grant", + "entitlement_digest": "sha256:39f793146c7d0523fe0a0267640093d3a124f3efa49ce2bd128075a7056fc4e7", + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "escalation_path": "human_approval_required", + "evidence_refs": [ + "request:req-2026-06-23-001", + "policy:access-policy-demo-v1", + "entitlements:user-17" + ], + "least_privilege_scope": "logs.read:prod/payments/service-a", + "policy_digest": "sha256:fed71a99fc94be00827e5a8fc8678ee5d07b3ff9fb2830bc3b718cff29bb8a9c", + "policy_id": "access-policy-demo-v1", + "reasons": [ + "request matches allowed role, action, resource, and scope prefix", + "ttl bounded to 120 minutes by policy max 240", + "proposal is gated; no access is issued by this skill" + ], + "request_digest": "sha256:42e9ee73c27845ed1fea00efce5f1d509b9bb97322d8c891ed74a78a14681d5a", + "request_id": "req-2026-06-23-001", + "requested_scope": "logs.read:prod/payments/service-a/*", + "requester_roles": [ + "oncall_engineer" + ], + "resource": "prod/payments/logs/service-a", + "subject_id": "user-17", + "ttl_minutes": 120 + }, + "schema": "frantic.delivery.evidence.v1" + }, + "grant_proposal": { + "action": "read", + "approval_gate": "human_approval_required", + "execution_status": "proposal_only", + "handoff": { + "catalog_skill": "least-privilege-auditor", + "requires_human_approval": true + }, + "issued_by_skill": false, + "justification": "Investigate incident INC-2042 elevated payment retries.", + "policy_id": "access-policy-demo-v1", + "proposal_id": "grant-proposal-req-2026-06-23-001", + "resource": "prod/payments/logs/service-a", + "schema": "runx.security.one_time_grant_proposal.v1", + "scope": "logs.read:prod/payments/service-a", + "subject_id": "user-17", + "ticket_id": "INC-2042", + "ttl_minutes": 120 + }, + "report": "# access-request-review report\n\nRequest: req-2026-06-23-001\nSubject: user-17\nPolicy: access-policy-demo-v1\nDecision: grant\nResource: prod/payments/logs/service-a\nRequested scope: logs.read:prod/payments/service-a/*\nLeast-privilege scope: logs.read:prod/payments/service-a\nTTL: 120 minutes\nApproval gate: human_approval_required\nEscalation: human_approval_required\n\n## Reasons\n- request matches allowed role, action, resource, and scope prefix\n- ttl bounded to 120 minutes by policy max 240\n- proposal is gated; no access is issued by this skill\n\nGrant proposal grant-proposal-req-2026-06-23-001 is proposal-only and requires human_approval_required.\nThis skill does not mutate grants, move secrets, or call identity-provider APIs." + }, + "receipt": { + "acts": [ + { + "artifact_refs": [], + "closure": { + "closed_at": "2026-06-23T05:01:03.689Z", + "disposition": "closed", + "reason_code": "process_exit", + "summary": "cli-tool exited successfully" + }, + "criterion_bindings": [ + { + "criterion_id": "process_exit", + "evidence_refs": [], + "status": "verified", + "summary": "cli-tool exited successfully", + "verification_refs": [] + } + ], + "form": "observation", + "id": "act_default", + "intent": { + "constraints": [], + "derived_from": [], + "legitimacy": "Runtime graph execution was admitted by the local harness", + "purpose": "Run graph step default", + "success_criteria": [ + { + "criterion_id": "process_exit", + "required": true, + "statement": "cli-tool exits successfully" + } + ] + }, + "source_refs": [], + "summary": "Executed graph step default", + "target_refs": [] + } + ], + "authority": { + "actor_ref": { + "type": "principal", + "uri": "runx:principal:local_runtime" + }, + "attenuation": { + "parent_authority_ref": null, + "subset_proof": null + }, + "authority_proof_refs": [], + "enforcement": { + "profile_hash": "sha256:runtime-skeleton-enforcement", + "redaction_refs": [], + "setup_refs": [], + "teardown_refs": [] + }, + "grant_refs": [], + "scope_refs": [], + "terms": [] + }, + "canonicalization": "runx.receipt.c14n.v1", + "created_at": "2026-06-23T05:01:03.689Z", + "decisions": [ + { + "artifact_refs": [], + "choice": "open", + "closure": null, + "decision_id": "dec_default", + "inputs": { + "opportunity_refs": [], + "selection_ref": null, + "signal_refs": [], + "target_ref": null + }, + "justification": { + "evidence_refs": [], + "summary": "runtime graph planner selected this node" + }, + "proposed_intent": { + "constraints": [], + "derived_from": [], + "legitimacy": "Local graph execution requested this node", + "purpose": "Open runtime node default", + "success_criteria": [] + }, + "selected_act_id": "act_default", + "selected_harness_ref": null + } + ], + "digest": "sha256:f17ca72b4a6a3c8c5d6498b655fa1adcb62b04b0dc30a5f291e6e1b8ae4cdbc1", + "id": "sha256:e7345fa793916faca7cad087176a959b36bbf794d35ed9e56a587eb2f9a1ac21", + "idempotency": { + "content_hash": "sha256:run_default_8e438e086dc8-default-content", + "intent_key": "sha256:run_default_8e438e086dc8-default-intent", + "trigger_fingerprint": "sha256:run_default_8e438e086dc8-default-trigger" + }, + "issuer": { + "kid": "local:bountybar-frantic-runx-22-20260618192655", + "public_key_sha256": "sha256:afae74faf1b1b104b27d788bee2dd0982052af89a1ae30b3282f1a14b0707871", + "type": "hosted" + }, + "lineage": { + "children": [], + "sync": [] + }, + "schema": "runx.receipt.v1", + "seal": { + "closed_at": "2026-06-23T05:01:03.689Z", + "criteria": [ + { + "criterion_id": "process_exit", + "evidence_refs": [], + "status": "verified", + "summary": "cli-tool exited successfully", + "verification_refs": [] + } + ], + "disposition": "closed", + "last_observed_at": "2026-06-23T05:01:03.689Z", + "reason_code": "process_closed", + "summary": "cli-tool default completed" + }, + "signals": [], + "signature": { + "alg": "Ed25519", + "value": "base64:PtZ--prYv24pqqw_aqkHatYeAnfeqJxNjgG3r3_RTIs0DC_O3xS1qexsVlVpHCybiJuyN9t2CPA7x5P1YviVDA" + }, + "subject": { + "commitments": [], + "kind": "skill", + "ref": { + "type": "harness", + "uri": "hrn_run_default_8e438e086dc8_default" + } + } + }, + "receipt_id": "sha256:e7345fa793916faca7cad087176a959b36bbf794d35ed9e56a587eb2f9a1ac21", + "run_id": "run_default_8e438e086dc8", + "schema": "runx.skill_run.v1", + "skill_name": "access-request-review", + "status": "sealed" +} diff --git a/skills/access-request-review/evidence/dogfood-output.stderr.txt b/skills/access-request-review/evidence/dogfood-output.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/skills/access-request-review/evidence/dogfood-receipts-linux.tgz b/skills/access-request-review/evidence/dogfood-receipts-linux.tgz new file mode 100644 index 00000000..cef953d8 Binary files /dev/null and b/skills/access-request-review/evidence/dogfood-receipts-linux.tgz differ diff --git a/skills/access-request-review/evidence/evidence.json b/skills/access-request-review/evidence/evidence.json new file mode 100644 index 00000000..b63434f1 --- /dev/null +++ b/skills/access-request-review/evidence/evidence.json @@ -0,0 +1,151 @@ +{ + "schema": "frantic.delivery.evidence.v1", + "summary": "access-request-review was published as a hosted runx skill, clean-installed from the registry, harnessed from the installed package, dogfooded on a bounded least-privilege access request, and verified with a sealed production-mode runx receipt.", + "artifact": "access-request-review", + "package": { + "owner": "lubuseb", + "name": "access-request-review", + "version": "sha-2100c1996336", + "registry_ref": "lubuseb/access-request-review@sha-2100c1996336", + "public_url": "https://runx.ai/x/lubuseb/access-request-review@sha-2100c1996336", + "registry_public_url": "https://runx.ai/x/lubuseb/access-request-review", + "digest": "5d7e4a0c2122f7f98d418827f0115f81c158004ac9a12f2236ba6eb212d60aee", + "profile_digest": "865d7c9c5be962b752080face77831ddbf4d110b6fae85ee40baba3dae6b436e" + }, + "source": { + "pr_url": "https://github.com/runxhq/runx/pull/123", + "source_url": "https://github.com/LubuSeb/runx/tree/access-request-review-55", + "x_yaml": "https://raw.githubusercontent.com/LubuSeb/runx/access-request-review-55/skills/access-request-review/X.yaml", + "skill_md": "https://raw.githubusercontent.com/LubuSeb/runx/access-request-review-55/skills/access-request-review/SKILL.md" + }, + "runtime": { + "runx_version": "runx-cli 0.6.13", + "receipt_ref": "runx:receipt:sha256:a4fd591f7cc19db6843c35c1f25dbed748e6eb8f2cd0b417707c51d6e0efb7ba", + "receipt_id": "sha256:a4fd591f7cc19db6843c35c1f25dbed748e6eb8f2cd0b417707c51d6e0efb7ba", + "verify_verdict": "valid", + "signature_mode": "production" + }, + "dogfood": { + "command": "runx skill lubuseb/access-request-review@sha-2100c1996336 --registry https://api.runx.ai --input-json access_request= --input-json policy= --input-json current_entitlements= --json", + "package": "lubuseb/access-request-review@sha-2100c1996336", + "input": "fixtures/grant-request.json", + "receipt_ref": "runx:receipt:sha256:a4fd591f7cc19db6843c35c1f25dbed748e6eb8f2cd0b417707c51d6e0efb7ba", + "verification_json": "hosted-verification.json", + "verify_verdict": "valid", + "harness_cases": [ + { + "name": "least-privilege-grant-proposal", + "status": "sealed" + }, + { + "name": "deny-for-disallowed-resource", + "status": "sealed" + }, + { + "name": "missing-justification-fails-closed", + "status": "refused" + } + ], + "decision": "grant", + "least_privilege_scope": "logs.read:prod/payments/service-a", + "ttl_minutes": 120, + "approval_gate": "human_approval_required", + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "execution_status": "proposal_only", + "safeguards": [ + "does not issue access", + "does not call identity providers", + "does not move secrets", + "requires human approval for the one-time grant proposal" + ] + }, + "observations": [ + { + "name": "runx_version", + "status": "passed", + "detail": "npx --yes @runxhq/cli@latest --version returned: runx-cli 0.6.13", + "evidence_ref": "runx-version.txt" + }, + { + "name": "published_registry_package", + "status": "passed", + "detail": "runx registry publish completed successfully for lubuseb/access-request-review@sha-2100c1996336 on https://api.runx.ai.", + "evidence_ref": "registry-publish.json" + }, + { + "name": "public_url_binding", + "status": "passed", + "detail": "Submitted public_url is the version-pinned registry page https://runx.ai/x/lubuseb/access-request-review@sha-2100c1996336; registry-publish.json also reports the unversioned landing page https://runx.ai/x/lubuseb/access-request-review.", + "evidence_ref": "registry-publish.json" + }, + { + "name": "registry_read", + "status": "passed", + "detail": "runx registry read resolved the published package metadata.", + "evidence_ref": "registry-read.json" + }, + { + "name": "clean_install", + "status": "passed", + "detail": "runx add installed lubuseb/access-request-review@sha-2100c1996336 into a clean directory.", + "evidence_ref": "clean-install.json" + }, + { + "name": "hosted_harness", + "status": "passed", + "detail": "The installed registry package passed least-privilege grant, deny, and fail-closed harness cases: least-privilege-grant-proposal=sealed, deny-for-disallowed-resource=sealed, missing-justification-fails-closed=refused.", + "evidence_ref": "hosted-harness.json" + }, + { + "name": "harness_case_names", + "status": "passed", + "detail": "Harness cases: least-privilege-grant-proposal=sealed; deny-for-disallowed-resource=sealed; missing-justification-fails-closed=refused.", + "evidence_ref": "hosted-harness.json" + }, + { + "name": "dogfood_run", + "status": "closed", + "detail": "Hosted dogfood emitted a grant proposal with a bounded TTL and human approval gate, without issuing access.", + "evidence_ref": "hosted-dogfood-output.json" + }, + { + "name": "escalation_path", + "status": "passed", + "detail": "Dogfood grant proposal escalates to human_approval_required and remains proposal-only; the skill never issues live access.", + "evidence_ref": "hosted-dogfood-output.json" + }, + { + "name": "dogfood_receipt_verified", + "status": "passed", + "detail": "runx verify accepted receipt sha256:a4fd591f7cc19db6843c35c1f25dbed748e6eb8f2cd0b417707c51d6e0efb7ba with no findings.", + "evidence_ref": "hosted-verification.json" + }, + { + "name": "receipt_id", + "status": "passed", + "detail": "Post-publish dogfood receipt id: sha256:a4fd591f7cc19db6843c35c1f25dbed748e6eb8f2cd0b417707c51d6e0efb7ba.", + "evidence_ref": "hosted-verification.json" + }, + { + "name": "read_only_behavior", + "status": "passed", + "detail": "The runner reads JSON inputs, computes deterministically in memory, performs no provider calls, requires no secrets, and emits structured stdout only.", + "evidence_ref": "run.mjs" + } + ], + "commands": [ + "npx --yes @runxhq/cli@latest --version", + "npx --yes @runxhq/cli@latest harness /mnt/f/BountyBar/repos/runx/skills/access-request-review --json", + "npx --yes @runxhq/cli@latest registry publish /mnt/f/BountyBar/repos/runx/skills/access-request-review/SKILL.md --registry https://api.runx.ai --json", + "npx --yes @runxhq/cli@latest registry read lubuseb/access-request-review@sha-2100c1996336 --registry https://api.runx.ai --json", + "npx --yes @runxhq/cli@latest add lubuseb/access-request-review@sha-2100c1996336 --registry https://api.runx.ai --to /tmp/runx-access-request-review-clean-install --json", + "npx --yes @runxhq/cli@latest harness /tmp/runx-access-request-review-clean-install/lubuseb/access-request-review/ --json", + "npx --yes @runxhq/cli@latest skill lubuseb/access-request-review@sha-2100c1996336 --registry https://api.runx.ai --input-json access_request= --input-json policy= --input-json current_entitlements= --json", + "npx --yes @runxhq/cli@latest verify --receipt-dir /tmp/runx-access-request-review-hosted-dogfood-receipts --json" + ] +} diff --git a/skills/access-request-review/evidence/hosted-dogfood-output.json b/skills/access-request-review/evidence/hosted-dogfood-output.json new file mode 100644 index 00000000..9080ce04 --- /dev/null +++ b/skills/access-request-review/evidence/hosted-dogfood-output.json @@ -0,0 +1,476 @@ +{ + "closure": { + "closed_at": "2026-06-23T05:01:17.986Z", + "disposition": "closed", + "reason_code": "process_closed", + "summary": "cli-tool default completed" + }, + "execution": { + "exit_code": 0, + "skill_claim": { + "decision_packet": { + "action": "read", + "approval_gate": "human_approval_required", + "decision": "grant", + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "evidence_refs": [ + "request:req-2026-06-23-001", + "policy:access-policy-demo-v1", + "entitlements:user-17" + ], + "least_privilege_scope": "logs.read:prod/payments/service-a", + "objective": "Hosted dogfood access request review for Frantic bounty 55.", + "policy_id": "access-policy-demo-v1", + "reasons": [ + "request matches allowed role, action, resource, and scope prefix", + "ttl bounded to 120 minutes by policy max 240", + "proposal is gated; no access is issued by this skill" + ], + "request_id": "req-2026-06-23-001", + "requested_scope": "logs.read:prod/payments/service-a/*", + "resource": "prod/payments/logs/service-a", + "safeguards": { + "executes_grant": false, + "mutates_grants": false, + "read_only": true, + "requires_human_approval": true + }, + "schema": "runx.security.access_request_review.v1", + "subject_id": "user-17", + "ttl_minutes": 120 + }, + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "evidence_json": { + "artifact": "access-request-review", + "observations": { + "action": "read", + "approval_gate": "human_approval_required", + "current_grant_count": 1, + "decision": "grant", + "entitlement_digest": "sha256:39f793146c7d0523fe0a0267640093d3a124f3efa49ce2bd128075a7056fc4e7", + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "escalation_path": "human_approval_required", + "evidence_refs": [ + "request:req-2026-06-23-001", + "policy:access-policy-demo-v1", + "entitlements:user-17" + ], + "least_privilege_scope": "logs.read:prod/payments/service-a", + "policy_digest": "sha256:fed71a99fc94be00827e5a8fc8678ee5d07b3ff9fb2830bc3b718cff29bb8a9c", + "policy_id": "access-policy-demo-v1", + "reasons": [ + "request matches allowed role, action, resource, and scope prefix", + "ttl bounded to 120 minutes by policy max 240", + "proposal is gated; no access is issued by this skill" + ], + "request_digest": "sha256:42e9ee73c27845ed1fea00efce5f1d509b9bb97322d8c891ed74a78a14681d5a", + "request_id": "req-2026-06-23-001", + "requested_scope": "logs.read:prod/payments/service-a/*", + "requester_roles": [ + "oncall_engineer" + ], + "resource": "prod/payments/logs/service-a", + "subject_id": "user-17", + "ttl_minutes": 120 + }, + "schema": "frantic.delivery.evidence.v1" + }, + "grant_proposal": { + "action": "read", + "approval_gate": "human_approval_required", + "execution_status": "proposal_only", + "handoff": { + "catalog_skill": "least-privilege-auditor", + "requires_human_approval": true + }, + "issued_by_skill": false, + "justification": "Investigate incident INC-2042 elevated payment retries.", + "policy_id": "access-policy-demo-v1", + "proposal_id": "grant-proposal-req-2026-06-23-001", + "resource": "prod/payments/logs/service-a", + "schema": "runx.security.one_time_grant_proposal.v1", + "scope": "logs.read:prod/payments/service-a", + "subject_id": "user-17", + "ticket_id": "INC-2042", + "ttl_minutes": 120 + }, + "report": "# access-request-review report\n\nRequest: req-2026-06-23-001\nSubject: user-17\nPolicy: access-policy-demo-v1\nDecision: grant\nResource: prod/payments/logs/service-a\nRequested scope: logs.read:prod/payments/service-a/*\nLeast-privilege scope: logs.read:prod/payments/service-a\nTTL: 120 minutes\nApproval gate: human_approval_required\nEscalation: human_approval_required\n\n## Reasons\n- request matches allowed role, action, resource, and scope prefix\n- ttl bounded to 120 minutes by policy max 240\n- proposal is gated; no access is issued by this skill\n\nGrant proposal grant-proposal-req-2026-06-23-001 is proposal-only and requires human_approval_required.\nThis skill does not mutate grants, move secrets, or call identity-provider APIs." + }, + "stderr": "", + "stdout": "{\n \"decision_packet\": {\n \"schema\": \"runx.security.access_request_review.v1\",\n \"decision\": \"grant\",\n \"request_id\": \"req-2026-06-23-001\",\n \"subject_id\": \"user-17\",\n \"objective\": \"Hosted dogfood access request review for Frantic bounty 55.\",\n \"policy_id\": \"access-policy-demo-v1\",\n \"resource\": \"prod/payments/logs/service-a\",\n \"action\": \"read\",\n \"requested_scope\": \"logs.read:prod/payments/service-a/*\",\n \"least_privilege_scope\": \"logs.read:prod/payments/service-a\",\n \"ttl_minutes\": 120,\n \"approval_gate\": \"human_approval_required\",\n \"escalation\": {\n \"required\": true,\n \"lane\": \"human_approval_required\",\n \"reason\": \"one-time grant proposal requires human approval before any access is issued\",\n \"ticket_id\": \"INC-2042\"\n },\n \"reasons\": [\n \"request matches allowed role, action, resource, and scope prefix\",\n \"ttl bounded to 120 minutes by policy max 240\",\n \"proposal is gated; no access is issued by this skill\"\n ],\n \"evidence_refs\": [\n \"request:req-2026-06-23-001\",\n \"policy:access-policy-demo-v1\",\n \"entitlements:user-17\"\n ],\n \"safeguards\": {\n \"read_only\": true,\n \"mutates_grants\": false,\n \"executes_grant\": false,\n \"requires_human_approval\": true\n }\n },\n \"grant_proposal\": {\n \"schema\": \"runx.security.one_time_grant_proposal.v1\",\n \"proposal_id\": \"grant-proposal-req-2026-06-23-001\",\n \"subject_id\": \"user-17\",\n \"action\": \"read\",\n \"resource\": \"prod/payments/logs/service-a\",\n \"scope\": \"logs.read:prod/payments/service-a\",\n \"ttl_minutes\": 120,\n \"approval_gate\": \"human_approval_required\",\n \"ticket_id\": \"INC-2042\",\n \"justification\": \"Investigate incident INC-2042 elevated payment retries.\",\n \"policy_id\": \"access-policy-demo-v1\",\n \"issued_by_skill\": false,\n \"execution_status\": \"proposal_only\",\n \"handoff\": {\n \"catalog_skill\": \"least-privilege-auditor\",\n \"requires_human_approval\": true\n }\n },\n \"escalation\": {\n \"required\": true,\n \"lane\": \"human_approval_required\",\n \"reason\": \"one-time grant proposal requires human approval before any access is issued\",\n \"ticket_id\": \"INC-2042\"\n },\n \"evidence_json\": {\n \"schema\": \"frantic.delivery.evidence.v1\",\n \"artifact\": \"access-request-review\",\n \"observations\": {\n \"request_id\": \"req-2026-06-23-001\",\n \"policy_id\": \"access-policy-demo-v1\",\n \"policy_digest\": \"sha256:fed71a99fc94be00827e5a8fc8678ee5d07b3ff9fb2830bc3b718cff29bb8a9c\",\n \"request_digest\": \"sha256:42e9ee73c27845ed1fea00efce5f1d509b9bb97322d8c891ed74a78a14681d5a\",\n \"entitlement_digest\": \"sha256:39f793146c7d0523fe0a0267640093d3a124f3efa49ce2bd128075a7056fc4e7\",\n \"subject_id\": \"user-17\",\n \"requester_roles\": [\n \"oncall_engineer\"\n ],\n \"action\": \"read\",\n \"resource\": \"prod/payments/logs/service-a\",\n \"requested_scope\": \"logs.read:prod/payments/service-a/*\",\n \"decision\": \"grant\",\n \"least_privilege_scope\": \"logs.read:prod/payments/service-a\",\n \"ttl_minutes\": 120,\n \"approval_gate\": \"human_approval_required\",\n \"escalation\": {\n \"required\": true,\n \"lane\": \"human_approval_required\",\n \"reason\": \"one-time grant proposal requires human approval before any access is issued\",\n \"ticket_id\": \"INC-2042\"\n },\n \"escalation_path\": \"human_approval_required\",\n \"current_grant_count\": 1,\n \"reasons\": [\n \"request matches allowed role, action, resource, and scope prefix\",\n \"ttl bounded to 120 minutes by policy max 240\",\n \"proposal is gated; no access is issued by this skill\"\n ],\n \"evidence_refs\": [\n \"request:req-2026-06-23-001\",\n \"policy:access-policy-demo-v1\",\n \"entitlements:user-17\"\n ]\n }\n },\n \"report\": \"# access-request-review report\\n\\nRequest: req-2026-06-23-001\\nSubject: user-17\\nPolicy: access-policy-demo-v1\\nDecision: grant\\nResource: prod/payments/logs/service-a\\nRequested scope: logs.read:prod/payments/service-a/*\\nLeast-privilege scope: logs.read:prod/payments/service-a\\nTTL: 120 minutes\\nApproval gate: human_approval_required\\nEscalation: human_approval_required\\n\\n## Reasons\\n- request matches allowed role, action, resource, and scope prefix\\n- ttl bounded to 120 minutes by policy max 240\\n- proposal is gated; no access is issued by this skill\\n\\nGrant proposal grant-proposal-req-2026-06-23-001 is proposal-only and requires human_approval_required.\\nThis skill does not mutate grants, move secrets, or call identity-provider APIs.\"\n}\n", + "structured_output": { + "decision_packet": { + "action": "read", + "approval_gate": "human_approval_required", + "decision": "grant", + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "evidence_refs": [ + "request:req-2026-06-23-001", + "policy:access-policy-demo-v1", + "entitlements:user-17" + ], + "least_privilege_scope": "logs.read:prod/payments/service-a", + "objective": "Hosted dogfood access request review for Frantic bounty 55.", + "policy_id": "access-policy-demo-v1", + "reasons": [ + "request matches allowed role, action, resource, and scope prefix", + "ttl bounded to 120 minutes by policy max 240", + "proposal is gated; no access is issued by this skill" + ], + "request_id": "req-2026-06-23-001", + "requested_scope": "logs.read:prod/payments/service-a/*", + "resource": "prod/payments/logs/service-a", + "safeguards": { + "executes_grant": false, + "mutates_grants": false, + "read_only": true, + "requires_human_approval": true + }, + "schema": "runx.security.access_request_review.v1", + "subject_id": "user-17", + "ttl_minutes": 120 + }, + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "evidence_json": { + "artifact": "access-request-review", + "observations": { + "action": "read", + "approval_gate": "human_approval_required", + "current_grant_count": 1, + "decision": "grant", + "entitlement_digest": "sha256:39f793146c7d0523fe0a0267640093d3a124f3efa49ce2bd128075a7056fc4e7", + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "escalation_path": "human_approval_required", + "evidence_refs": [ + "request:req-2026-06-23-001", + "policy:access-policy-demo-v1", + "entitlements:user-17" + ], + "least_privilege_scope": "logs.read:prod/payments/service-a", + "policy_digest": "sha256:fed71a99fc94be00827e5a8fc8678ee5d07b3ff9fb2830bc3b718cff29bb8a9c", + "policy_id": "access-policy-demo-v1", + "reasons": [ + "request matches allowed role, action, resource, and scope prefix", + "ttl bounded to 120 minutes by policy max 240", + "proposal is gated; no access is issued by this skill" + ], + "request_digest": "sha256:42e9ee73c27845ed1fea00efce5f1d509b9bb97322d8c891ed74a78a14681d5a", + "request_id": "req-2026-06-23-001", + "requested_scope": "logs.read:prod/payments/service-a/*", + "requester_roles": [ + "oncall_engineer" + ], + "resource": "prod/payments/logs/service-a", + "subject_id": "user-17", + "ttl_minutes": 120 + }, + "schema": "frantic.delivery.evidence.v1" + }, + "grant_proposal": { + "action": "read", + "approval_gate": "human_approval_required", + "execution_status": "proposal_only", + "handoff": { + "catalog_skill": "least-privilege-auditor", + "requires_human_approval": true + }, + "issued_by_skill": false, + "justification": "Investigate incident INC-2042 elevated payment retries.", + "policy_id": "access-policy-demo-v1", + "proposal_id": "grant-proposal-req-2026-06-23-001", + "resource": "prod/payments/logs/service-a", + "schema": "runx.security.one_time_grant_proposal.v1", + "scope": "logs.read:prod/payments/service-a", + "subject_id": "user-17", + "ticket_id": "INC-2042", + "ttl_minutes": 120 + }, + "report": "# access-request-review report\n\nRequest: req-2026-06-23-001\nSubject: user-17\nPolicy: access-policy-demo-v1\nDecision: grant\nResource: prod/payments/logs/service-a\nRequested scope: logs.read:prod/payments/service-a/*\nLeast-privilege scope: logs.read:prod/payments/service-a\nTTL: 120 minutes\nApproval gate: human_approval_required\nEscalation: human_approval_required\n\n## Reasons\n- request matches allowed role, action, resource, and scope prefix\n- ttl bounded to 120 minutes by policy max 240\n- proposal is gated; no access is issued by this skill\n\nGrant proposal grant-proposal-req-2026-06-23-001 is proposal-only and requires human_approval_required.\nThis skill does not mutate grants, move secrets, or call identity-provider APIs." + } + }, + "payload": { + "decision_packet": { + "action": "read", + "approval_gate": "human_approval_required", + "decision": "grant", + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "evidence_refs": [ + "request:req-2026-06-23-001", + "policy:access-policy-demo-v1", + "entitlements:user-17" + ], + "least_privilege_scope": "logs.read:prod/payments/service-a", + "objective": "Hosted dogfood access request review for Frantic bounty 55.", + "policy_id": "access-policy-demo-v1", + "reasons": [ + "request matches allowed role, action, resource, and scope prefix", + "ttl bounded to 120 minutes by policy max 240", + "proposal is gated; no access is issued by this skill" + ], + "request_id": "req-2026-06-23-001", + "requested_scope": "logs.read:prod/payments/service-a/*", + "resource": "prod/payments/logs/service-a", + "safeguards": { + "executes_grant": false, + "mutates_grants": false, + "read_only": true, + "requires_human_approval": true + }, + "schema": "runx.security.access_request_review.v1", + "subject_id": "user-17", + "ttl_minutes": 120 + }, + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "evidence_json": { + "artifact": "access-request-review", + "observations": { + "action": "read", + "approval_gate": "human_approval_required", + "current_grant_count": 1, + "decision": "grant", + "entitlement_digest": "sha256:39f793146c7d0523fe0a0267640093d3a124f3efa49ce2bd128075a7056fc4e7", + "escalation": { + "lane": "human_approval_required", + "reason": "one-time grant proposal requires human approval before any access is issued", + "required": true, + "ticket_id": "INC-2042" + }, + "escalation_path": "human_approval_required", + "evidence_refs": [ + "request:req-2026-06-23-001", + "policy:access-policy-demo-v1", + "entitlements:user-17" + ], + "least_privilege_scope": "logs.read:prod/payments/service-a", + "policy_digest": "sha256:fed71a99fc94be00827e5a8fc8678ee5d07b3ff9fb2830bc3b718cff29bb8a9c", + "policy_id": "access-policy-demo-v1", + "reasons": [ + "request matches allowed role, action, resource, and scope prefix", + "ttl bounded to 120 minutes by policy max 240", + "proposal is gated; no access is issued by this skill" + ], + "request_digest": "sha256:42e9ee73c27845ed1fea00efce5f1d509b9bb97322d8c891ed74a78a14681d5a", + "request_id": "req-2026-06-23-001", + "requested_scope": "logs.read:prod/payments/service-a/*", + "requester_roles": [ + "oncall_engineer" + ], + "resource": "prod/payments/logs/service-a", + "subject_id": "user-17", + "ttl_minutes": 120 + }, + "schema": "frantic.delivery.evidence.v1" + }, + "grant_proposal": { + "action": "read", + "approval_gate": "human_approval_required", + "execution_status": "proposal_only", + "handoff": { + "catalog_skill": "least-privilege-auditor", + "requires_human_approval": true + }, + "issued_by_skill": false, + "justification": "Investigate incident INC-2042 elevated payment retries.", + "policy_id": "access-policy-demo-v1", + "proposal_id": "grant-proposal-req-2026-06-23-001", + "resource": "prod/payments/logs/service-a", + "schema": "runx.security.one_time_grant_proposal.v1", + "scope": "logs.read:prod/payments/service-a", + "subject_id": "user-17", + "ticket_id": "INC-2042", + "ttl_minutes": 120 + }, + "report": "# access-request-review report\n\nRequest: req-2026-06-23-001\nSubject: user-17\nPolicy: access-policy-demo-v1\nDecision: grant\nResource: prod/payments/logs/service-a\nRequested scope: logs.read:prod/payments/service-a/*\nLeast-privilege scope: logs.read:prod/payments/service-a\nTTL: 120 minutes\nApproval gate: human_approval_required\nEscalation: human_approval_required\n\n## Reasons\n- request matches allowed role, action, resource, and scope prefix\n- ttl bounded to 120 minutes by policy max 240\n- proposal is gated; no access is issued by this skill\n\nGrant proposal grant-proposal-req-2026-06-23-001 is proposal-only and requires human_approval_required.\nThis skill does not mutate grants, move secrets, or call identity-provider APIs." + }, + "receipt": { + "acts": [ + { + "artifact_refs": [], + "closure": { + "closed_at": "2026-06-23T05:01:17.986Z", + "disposition": "closed", + "reason_code": "process_exit", + "summary": "cli-tool exited successfully" + }, + "criterion_bindings": [ + { + "criterion_id": "process_exit", + "evidence_refs": [], + "status": "verified", + "summary": "cli-tool exited successfully", + "verification_refs": [] + } + ], + "form": "observation", + "id": "act_default", + "intent": { + "constraints": [], + "derived_from": [], + "legitimacy": "Runtime graph execution was admitted by the local harness", + "purpose": "Run graph step default", + "success_criteria": [ + { + "criterion_id": "process_exit", + "required": true, + "statement": "cli-tool exits successfully" + } + ] + }, + "source_refs": [], + "summary": "Executed graph step default", + "target_refs": [] + } + ], + "authority": { + "actor_ref": { + "type": "principal", + "uri": "runx:principal:local_runtime" + }, + "attenuation": { + "parent_authority_ref": null, + "subset_proof": null + }, + "authority_proof_refs": [], + "enforcement": { + "profile_hash": "sha256:runtime-skeleton-enforcement", + "redaction_refs": [], + "setup_refs": [], + "teardown_refs": [] + }, + "grant_refs": [], + "scope_refs": [], + "terms": [] + }, + "canonicalization": "runx.receipt.c14n.v1", + "created_at": "2026-06-23T05:01:17.986Z", + "decisions": [ + { + "artifact_refs": [], + "choice": "open", + "closure": null, + "decision_id": "dec_default", + "inputs": { + "opportunity_refs": [], + "selection_ref": null, + "signal_refs": [], + "target_ref": null + }, + "justification": { + "evidence_refs": [], + "summary": "runtime graph planner selected this node" + }, + "proposed_intent": { + "constraints": [], + "derived_from": [], + "legitimacy": "Local graph execution requested this node", + "purpose": "Open runtime node default", + "success_criteria": [] + }, + "selected_act_id": "act_default", + "selected_harness_ref": null + } + ], + "digest": "sha256:3dae333124cd272cca16d886a728ba88967b8c7c8c990bb338105b4339436915", + "id": "sha256:a4fd591f7cc19db6843c35c1f25dbed748e6eb8f2cd0b417707c51d6e0efb7ba", + "idempotency": { + "content_hash": "sha256:run_default_470fa655fc09-default-content", + "intent_key": "sha256:run_default_470fa655fc09-default-intent", + "trigger_fingerprint": "sha256:run_default_470fa655fc09-default-trigger" + }, + "issuer": { + "kid": "local:bountybar-frantic-runx-22-20260618192655", + "public_key_sha256": "sha256:afae74faf1b1b104b27d788bee2dd0982052af89a1ae30b3282f1a14b0707871", + "type": "hosted" + }, + "lineage": { + "children": [], + "sync": [] + }, + "schema": "runx.receipt.v1", + "seal": { + "closed_at": "2026-06-23T05:01:17.986Z", + "criteria": [ + { + "criterion_id": "process_exit", + "evidence_refs": [], + "status": "verified", + "summary": "cli-tool exited successfully", + "verification_refs": [] + } + ], + "disposition": "closed", + "last_observed_at": "2026-06-23T05:01:17.986Z", + "reason_code": "process_closed", + "summary": "cli-tool default completed" + }, + "signals": [], + "signature": { + "alg": "Ed25519", + "value": "base64:lyKf_YXHdammnW4HzXP2gHwm8rD4Mla3_x1ModRB1HX99RGC_TcNCk373gcP87Dzb0qe9jc6Dij4CfPDwLPpBg" + }, + "subject": { + "commitments": [], + "kind": "skill", + "ref": { + "type": "harness", + "uri": "hrn_run_default_470fa655fc09_default" + } + } + }, + "receipt_id": "sha256:a4fd591f7cc19db6843c35c1f25dbed748e6eb8f2cd0b417707c51d6e0efb7ba", + "registry_provenance": { + "digest": "sha256:5d7e4a0c2122f7f98d418827f0115f81c158004ac9a12f2236ba6eb212d60aee", + "profile_digest": "sha256:865d7c9c5be962b752080face77831ddbf4d110b6fae85ee40baba3dae6b436e", + "registry_key_id": "runx-registry-ed25519-v1", + "registry_source": "remote https://api.runx.ai", + "registry_source_fingerprint": "ba1ac16b631195fd", + "skill_id": "lubuseb/access-request-review", + "trust_state": "trusted", + "trust_tier": "community", + "version": "sha-2100c1996336" + }, + "run_id": "run_default_470fa655fc09", + "schema": "runx.skill_run.v1", + "skill_name": "access-request-review", + "status": "sealed" +} diff --git a/skills/access-request-review/evidence/hosted-dogfood-output.stderr.txt b/skills/access-request-review/evidence/hosted-dogfood-output.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/skills/access-request-review/evidence/hosted-dogfood-receipts-linux.tgz b/skills/access-request-review/evidence/hosted-dogfood-receipts-linux.tgz new file mode 100644 index 00000000..a08b090e Binary files /dev/null and b/skills/access-request-review/evidence/hosted-dogfood-receipts-linux.tgz differ diff --git a/skills/access-request-review/evidence/hosted-harness-receipts-linux.tgz b/skills/access-request-review/evidence/hosted-harness-receipts-linux.tgz new file mode 100644 index 00000000..904f713a Binary files /dev/null and b/skills/access-request-review/evidence/hosted-harness-receipts-linux.tgz differ diff --git a/skills/access-request-review/evidence/hosted-harness-verification.json b/skills/access-request-review/evidence/hosted-harness-verification.json new file mode 100644 index 00000000..15f56422 --- /dev/null +++ b/skills/access-request-review/evidence/hosted-harness-verification.json @@ -0,0 +1,29 @@ +{ + "receipt_dir": "/tmp/runx-access-request-review-hosted-harness-receipts", + "signature_mode": "production", + "trees": [ + { + "root_receipt_id": "sha256:0636c61c47e81f7eca6bf7a8f744503e3698289839cbcc11f1321cc758b10184", + "receipt_count": 1, + "parent_missing": null, + "valid": true, + "findings": [] + }, + { + "root_receipt_id": "sha256:0d0379e46e93b8a7f47276d68d557a4752a9f8c40ca78298320e3e46c61a7cb2", + "receipt_count": 1, + "parent_missing": null, + "valid": true, + "findings": [] + }, + { + "root_receipt_id": "sha256:c90b1a0af21feb8469dc3e5dd0b4ac5a01881fabd1e21c04c6a2230d680a504d", + "receipt_count": 1, + "parent_missing": null, + "valid": true, + "findings": [] + } + ], + "unreadable_files": [], + "valid": true +} diff --git a/skills/access-request-review/evidence/hosted-harness-verification.stderr.txt b/skills/access-request-review/evidence/hosted-harness-verification.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/skills/access-request-review/evidence/hosted-harness.json b/skills/access-request-review/evidence/hosted-harness.json new file mode 100644 index 00000000..c8ef0579 --- /dev/null +++ b/skills/access-request-review/evidence/hosted-harness.json @@ -0,0 +1,17 @@ +{ + "status": "passed", + "case_count": 3, + "assertion_error_count": 0, + "assertion_errors": [], + "case_names": [ + "least-privilege-grant-proposal", + "deny-for-disallowed-resource", + "missing-justification-fails-closed" + ], + "receipt_ids": [ + "sha256:0d0379e46e93b8a7f47276d68d557a4752a9f8c40ca78298320e3e46c61a7cb2", + "sha256:0636c61c47e81f7eca6bf7a8f744503e3698289839cbcc11f1321cc758b10184", + "sha256:c90b1a0af21feb8469dc3e5dd0b4ac5a01881fabd1e21c04c6a2230d680a504d" + ], + "graph_case_count": 0 +} diff --git a/skills/access-request-review/evidence/hosted-harness.stderr.txt b/skills/access-request-review/evidence/hosted-harness.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/skills/access-request-review/evidence/hosted-verification.json b/skills/access-request-review/evidence/hosted-verification.json new file mode 100644 index 00000000..7861c84d --- /dev/null +++ b/skills/access-request-review/evidence/hosted-verification.json @@ -0,0 +1,15 @@ +{ + "receipt_dir": "/tmp/runx-access-request-review-hosted-dogfood-receipts", + "signature_mode": "production", + "trees": [ + { + "root_receipt_id": "sha256:a4fd591f7cc19db6843c35c1f25dbed748e6eb8f2cd0b417707c51d6e0efb7ba", + "receipt_count": 1, + "parent_missing": null, + "valid": true, + "findings": [] + } + ], + "unreadable_files": [], + "valid": true +} diff --git a/skills/access-request-review/evidence/hosted-verification.stderr.txt b/skills/access-request-review/evidence/hosted-verification.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/skills/access-request-review/evidence/local-harness-receipts-linux.tgz b/skills/access-request-review/evidence/local-harness-receipts-linux.tgz new file mode 100644 index 00000000..ee8cf582 Binary files /dev/null and b/skills/access-request-review/evidence/local-harness-receipts-linux.tgz differ diff --git a/skills/access-request-review/evidence/local-harness-verification.json b/skills/access-request-review/evidence/local-harness-verification.json new file mode 100644 index 00000000..15605088 --- /dev/null +++ b/skills/access-request-review/evidence/local-harness-verification.json @@ -0,0 +1,29 @@ +{ + "receipt_dir": "/tmp/runx-access-request-review-harness-receipts", + "signature_mode": "production", + "trees": [ + { + "root_receipt_id": "sha256:80913055a7d490e01e3a27ca0dd6aa2ace475770b66537a1dc83c6c5b375fdfe", + "receipt_count": 1, + "parent_missing": null, + "valid": true, + "findings": [] + }, + { + "root_receipt_id": "sha256:944b199698a86fb54d84d79443a85ee2ad3cd36005ee6e3e4b92f0d1c0bccf7c", + "receipt_count": 1, + "parent_missing": null, + "valid": true, + "findings": [] + }, + { + "root_receipt_id": "sha256:960d6a9d5e0a90aa5a815e74cf50c101a85a88928347528dcc08db7b10f90eaa", + "receipt_count": 1, + "parent_missing": null, + "valid": true, + "findings": [] + } + ], + "unreadable_files": [], + "valid": true +} diff --git a/skills/access-request-review/evidence/local-harness-verification.stderr.txt b/skills/access-request-review/evidence/local-harness-verification.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/skills/access-request-review/evidence/local-harness.json b/skills/access-request-review/evidence/local-harness.json new file mode 100644 index 00000000..6942da98 --- /dev/null +++ b/skills/access-request-review/evidence/local-harness.json @@ -0,0 +1,17 @@ +{ + "status": "passed", + "case_count": 3, + "assertion_error_count": 0, + "assertion_errors": [], + "case_names": [ + "least-privilege-grant-proposal", + "deny-for-disallowed-resource", + "missing-justification-fails-closed" + ], + "receipt_ids": [ + "sha256:80913055a7d490e01e3a27ca0dd6aa2ace475770b66537a1dc83c6c5b375fdfe", + "sha256:960d6a9d5e0a90aa5a815e74cf50c101a85a88928347528dcc08db7b10f90eaa", + "sha256:944b199698a86fb54d84d79443a85ee2ad3cd36005ee6e3e4b92f0d1c0bccf7c" + ], + "graph_case_count": 0 +} diff --git a/skills/access-request-review/evidence/local-harness.stderr.txt b/skills/access-request-review/evidence/local-harness.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/skills/access-request-review/evidence/registry-publish.json b/skills/access-request-review/evidence/registry-publish.json new file mode 100644 index 00000000..16853d2c --- /dev/null +++ b/skills/access-request-review/evidence/registry-publish.json @@ -0,0 +1,20 @@ +{ + "status": "success", + "registry": { + "action": "publish", + "publish": { + "target": "hosted", + "status": "published", + "skill_id": "lubuseb/access-request-review", + "owner": "lubuseb", + "name": "access-request-review", + "version": "sha-2100c1996336", + "digest": "5d7e4a0c2122f7f98d418827f0115f81c158004ac9a12f2236ba6eb212d60aee", + "profile_digest": "865d7c9c5be962b752080face77831ddbf4d110b6fae85ee40baba3dae6b436e", + "trust_tier": "community", + "install_command": "runx add lubuseb/access-request-review@sha-2100c1996336 --registry https://api.runx.ai", + "run_command": "runx skill lubuseb/access-request-review@sha-2100c1996336 --registry https://api.runx.ai", + "public_url": "https://runx.ai/x/lubuseb/access-request-review" + } + } +} diff --git a/skills/access-request-review/evidence/registry-publish.stderr.txt b/skills/access-request-review/evidence/registry-publish.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/skills/access-request-review/evidence/registry-read.json b/skills/access-request-review/evidence/registry-read.json new file mode 100644 index 00000000..9a8d4cd4 --- /dev/null +++ b/skills/access-request-review/evidence/registry-read.json @@ -0,0 +1,50 @@ +{ + "status": "success", + "registry": { + "action": "read", + "source": "remote", + "ref": "lubuseb/access-request-review@sha-2100c1996336", + "skill": { + "skill_id": "lubuseb/access-request-review", + "owner": "lubuseb", + "name": "access-request-review", + "description": "Review a bounded access request against policy and emit a least-privilege grant proposal or denial.", + "category": "security", + "version": "sha-2100c1996336", + "digest": "5d7e4a0c2122f7f98d418827f0115f81c158004ac9a12f2236ba6eb212d60aee", + "markdown": "---\nname: access-request-review\ndescription: Review a bounded access request against policy and emit a least-privilege grant proposal or denial.\nsource:\n type: cli-tool\n command: node\n args:\n - run.mjs\n input_mode: stdin\n cwd: .\n timeout_seconds: 30\ninputs:\n access_request:\n type: json\n required: true\n description: Request packet with requester, requested resource, requested action, business justification, and optional ticket metadata.\n policy:\n type: json\n required: true\n description: Access policy with allowed roles, resources, actions, TTL caps, approval rules, and escalation rules.\n current_entitlements:\n type: json\n required: true\n description: Current role and grant state for the requester.\n objective:\n type: string\n required: false\n description: Optional operator intent for the review.\nrunx:\n category: security\n input_resolution:\n required:\n - access_request\n - policy\n - current_entitlements\n---\n\n# access-request-review\n\nUse this skill when an operator needs a bounded access decision before a\nhuman-approved one-time grant. The skill compares a request, the governing\npolicy, and current entitlements, then returns `grant`, `deny`, or\n`needs_human_review`.\n\nThe skill never creates access, calls identity providers, sends approval\nmessages, stores credentials, or widens authority outside the supplied policy.\nWhen access is allowed it emits a least-privilege grant proposal with a bounded\nTTL, exact scope, approval gate, escalation lane, and evidence citations.\n\n## Inputs\n\n- `access_request`: requester id, role, action, resource, requested scope,\n justification, ticket id, and optional requested TTL.\n- `policy`: allowed roles, resources, actions, maximum TTL, denied resources,\n sensitive resources, required approvals, and break-glass rules.\n- `current_entitlements`: current grants and group/role state for the requester.\n- `objective`: optional operator intent.\n\n## Output\n\nThe runner returns JSON with:\n\n- `decision_packet` object: typed decision packet.\n- `grant_proposal` object: one-time proposal when the decision is `grant`.\n- `escalation` object: `required`, `lane`, `reason`, and optional `ticket_id`\n for human approval or denial escalation.\n- `evidence_json` object: compact review evidence for external verification.\n- `report` string: human-readable review summary.\n\nDecisions are deterministic and fail closed when request, policy, or entitlement\nfacts are missing.", + "profile_digest": "865d7c9c5be962b752080face77831ddbf4d110b6fae85ee40baba3dae6b436e", + "runner_names": [ + "default" + ], + "source_type": "cli-tool", + "trust_tier": "community", + "required_scopes": [], + "tags": [], + "publisher": { + "kind": "user", + "id": "user_53f00ae7ec2363e37ac6ff68", + "handle": "lubuseb", + "display_name": "LubuSeb" + }, + "attestations": [ + { + "kind": "publisher", + "id": "publisher:user_53f00ae7ec2363e37ac6ff68", + "status": "declared", + "summary": "LubuSeb", + "issued_at": "2026-06-23T05:01:11.882Z", + "metadata": { + "publisher_display_name": "LubuSeb", + "publisher_handle": "lubuseb", + "publisher_id": "user_53f00ae7ec2363e37ac6ff68", + "publisher_kind": "user", + "trust_tier": "community" + } + } + ], + "install_command": "runx add lubuseb/access-request-review@sha-2100c1996336 --registry https://api.runx.ai", + "run_command": "runx skill lubuseb/access-request-review@sha-2100c1996336 --registry https://api.runx.ai" + } + } +} diff --git a/skills/access-request-review/evidence/registry-read.stderr.txt b/skills/access-request-review/evidence/registry-read.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/skills/access-request-review/evidence/report.md b/skills/access-request-review/evidence/report.md new file mode 100644 index 00000000..c474f9ab --- /dev/null +++ b/skills/access-request-review/evidence/report.md @@ -0,0 +1,23 @@ +# access-request-review delivery report + +- Package: `lubuseb/access-request-review@sha-2100c1996336`. +- Public registry URL: `https://runx.ai/x/lubuseb/access-request-review@sha-2100c1996336`. +- Source PR: `https://github.com/runxhq/runx/pull/123`. +- `runx-cli 0.6.13` was used for publish, registry read, clean install, harness, dogfood, and receipt verification. +- Local harness passed three cases: `least-privilege-grant-proposal`, `deny-for-disallowed-resource`, and `missing-justification-fails-closed`. +- Hosted clean install succeeded with `runx add lubuseb/access-request-review@sha-2100c1996336 --registry https://api.runx.ai`. +- Harness from the clean installed package passed the same three cases and all hosted harness receipts verified. +- Hosted dogfood produced sealed receipt `runx:receipt:sha256:a4fd591f7cc19db6843c35c1f25dbed748e6eb8f2cd0b417707c51d6e0efb7ba`. +- `runx verify` returned `valid=true` and no findings for the dogfood receipt. +- The runner emits a bounded `one_time_grant_proposal` only when the request matches role, action, resource, scope prefix, TTL, and approval-gate policy. +- The implementation is read-only: it reads the provided JSON request, policy, and entitlements, computes deterministically in memory, requires no credentials, performs no network/provider calls, and emits structured stdout. + +Reproduce: + +```bash +runx add lubuseb/access-request-review@sha-2100c1996336 --registry https://api.runx.ai +runx registry read lubuseb/access-request-review@sha-2100c1996336 --registry https://api.runx.ai --json +runx harness ./access-request-review --json +runx skill lubuseb/access-request-review@sha-2100c1996336 --registry https://api.runx.ai --input-json access_request='' --input-json policy='' --input-json current_entitlements='' --json +runx verify --receipt-dir /tmp/runx-access-request-review-hosted-dogfood-receipts --json +``` diff --git a/skills/access-request-review/evidence/runx-version.txt b/skills/access-request-review/evidence/runx-version.txt new file mode 100644 index 00000000..6ff8172c --- /dev/null +++ b/skills/access-request-review/evidence/runx-version.txt @@ -0,0 +1 @@ +runx-cli 0.6.13 diff --git a/skills/access-request-review/evidence/verification.json b/skills/access-request-review/evidence/verification.json new file mode 100644 index 00000000..5940442e --- /dev/null +++ b/skills/access-request-review/evidence/verification.json @@ -0,0 +1,15 @@ +{ + "receipt_dir": "/tmp/runx-access-request-review-dogfood-receipts", + "signature_mode": "production", + "trees": [ + { + "root_receipt_id": "sha256:e7345fa793916faca7cad087176a959b36bbf794d35ed9e56a587eb2f9a1ac21", + "receipt_count": 1, + "parent_missing": null, + "valid": true, + "findings": [] + } + ], + "unreadable_files": [], + "valid": true +} diff --git a/skills/access-request-review/evidence/verification.stderr.txt b/skills/access-request-review/evidence/verification.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/skills/access-request-review/fixtures/deny-request.json b/skills/access-request-review/fixtures/deny-request.json new file mode 100644 index 00000000..2e20c161 --- /dev/null +++ b/skills/access-request-review/fixtures/deny-request.json @@ -0,0 +1,41 @@ +{ + "objective": "Reject direct secret access.", + "access_request": { + "request_id": "req-2026-06-23-002", + "requester": { + "id": "user-21", + "role": "oncall_engineer", + "team": "payments" + }, + "action": "read", + "resource": "prod/payments/secrets", + "requested_scope": "secrets.read:prod/payments/*", + "justification": "Need to check API keys.", + "ticket_id": "INC-2043", + "requested_ttl_minutes": 60 + }, + "policy": { + "policy_id": "access-policy-demo-v1", + "max_ttl_minutes": 240, + "allowed_roles": { + "oncall_engineer": { + "actions": ["read"], + "resources": ["prod/payments/logs/*"], + "scope_prefixes": ["logs.read:prod/payments/"] + } + }, + "denied_resources": ["prod/payments/secrets"], + "sensitive_resources": ["prod/payments/logs/*"], + "required_approvals": { + "sensitive_resource": "human_approval_required" + }, + "grant_defaults": { + "approval_gate": "human_approval_required" + } + }, + "current_entitlements": { + "subject_id": "user-21", + "roles": ["oncall_engineer"], + "current_grants": [] + } +} diff --git a/skills/access-request-review/fixtures/grant-request.json b/skills/access-request-review/fixtures/grant-request.json new file mode 100644 index 00000000..77c2ea2b --- /dev/null +++ b/skills/access-request-review/fixtures/grant-request.json @@ -0,0 +1,47 @@ +{ + "objective": "Review a temporary production log access request for incident response.", + "access_request": { + "request_id": "req-2026-06-23-001", + "requester": { + "id": "user-17", + "role": "oncall_engineer", + "team": "payments" + }, + "action": "read", + "resource": "prod/payments/logs/service-a", + "requested_scope": "logs.read:prod/payments/service-a/*", + "justification": "Investigate incident INC-2042 elevated payment retries.", + "ticket_id": "INC-2042", + "requested_ttl_minutes": 120 + }, + "policy": { + "policy_id": "access-policy-demo-v1", + "max_ttl_minutes": 240, + "allowed_roles": { + "oncall_engineer": { + "actions": ["read"], + "resources": ["prod/payments/logs/*"], + "scope_prefixes": ["logs.read:prod/payments/"] + } + }, + "denied_resources": ["prod/payments/secrets"], + "sensitive_resources": ["prod/payments/logs/*"], + "required_approvals": { + "sensitive_resource": "human_approval_required" + }, + "grant_defaults": { + "approval_gate": "human_approval_required" + } + }, + "current_entitlements": { + "subject_id": "user-17", + "roles": ["oncall_engineer"], + "current_grants": [ + { + "grant_id": "grant-staging-logs", + "scope": "logs.read:staging/payments/*", + "expires_at": "2026-06-23T12:00:00Z" + } + ] + } +} diff --git a/skills/access-request-review/run.mjs b/skills/access-request-review/run.mjs new file mode 100644 index 00000000..cd1bbc1a --- /dev/null +++ b/skills/access-request-review/run.mjs @@ -0,0 +1,356 @@ +import fs from "node:fs"; +import crypto from "node:crypto"; + +const inputs = readInputs(); +const request = requireObject(inputs.access_request, "access_request"); +const policy = requireObject(inputs.policy, "policy"); +const entitlements = requireObject(inputs.current_entitlements, "current_entitlements"); +const objective = stringValue(inputs.objective) || "Review access request."; + +const normalized = normalizeInputs(request, policy, entitlements); +const decision = decide(normalized); +const grantProposal = decision.decision === "grant" ? buildGrantProposal(normalized, decision) : null; +const escalation = buildEscalation(normalized, decision); + +const decisionPacket = { + schema: "runx.security.access_request_review.v1", + decision: decision.decision, + request_id: normalized.requestId, + subject_id: normalized.subjectId, + objective, + policy_id: normalized.policyId, + resource: normalized.resource, + action: normalized.action, + requested_scope: normalized.requestedScope, + least_privilege_scope: decision.leastPrivilegeScope, + ttl_minutes: decision.ttlMinutes, + approval_gate: decision.approvalGate, + escalation, + reasons: decision.reasons, + evidence_refs: decision.evidenceRefs, + safeguards: { + read_only: true, + mutates_grants: false, + executes_grant: false, + requires_human_approval: decision.decision === "grant", + }, +}; + +const evidenceJson = { + schema: "frantic.delivery.evidence.v1", + artifact: "access-request-review", + observations: { + request_id: normalized.requestId, + policy_id: normalized.policyId, + policy_digest: sha256Json(policy), + request_digest: sha256Json(request), + entitlement_digest: sha256Json(entitlements), + subject_id: normalized.subjectId, + requester_roles: normalized.roles, + action: normalized.action, + resource: normalized.resource, + requested_scope: normalized.requestedScope, + decision: decision.decision, + least_privilege_scope: decision.leastPrivilegeScope, + ttl_minutes: decision.ttlMinutes, + approval_gate: decision.approvalGate, + escalation, + escalation_path: escalation.lane, + current_grant_count: normalized.currentGrants.length, + reasons: decision.reasons, + evidence_refs: decision.evidenceRefs, + }, +}; + +const report = renderReport(decisionPacket, grantProposal); + +process.stdout.write( + `${JSON.stringify({ decision_packet: decisionPacket, grant_proposal: grantProposal, escalation, evidence_json: evidenceJson, report }, null, 2)}\n`, +); + +function normalizeInputs(request, policy, entitlements) { + const requestId = stringValue(request.request_id); + const requester = requireObject(request.requester, "access_request.requester"); + const subjectId = stringValue(requester.id); + const requesterRole = stringValue(requester.role); + const action = stringValue(request.action); + const resource = stringValue(request.resource); + const requestedScope = stringValue(request.requested_scope); + const justification = stringValue(request.justification); + const policyId = stringValue(policy.policy_id); + const maxTtlMinutes = numberValue(policy.max_ttl_minutes); + const requestedTtlMinutes = numberValue(request.requested_ttl_minutes); + const roles = [...new Set([requesterRole, ...arrayValue(entitlements.roles).map(String)].filter(Boolean))]; + const currentGrants = arrayValue(entitlements.current_grants).map((grant, index) => { + if (!isObject(grant)) throw new Error(`current_entitlements.current_grants[${index}] must be an object`); + return { + grant_id: stringValue(grant.grant_id) || `grant-${index + 1}`, + scope: stringValue(grant.scope) || "", + expires_at: stringValue(grant.expires_at), + }; + }); + + if (!requestId) throw new Error("access_request.request_id is required"); + if (!subjectId) throw new Error("access_request.requester.id is required"); + if (!requesterRole) throw new Error("access_request.requester.role is required"); + if (!action) throw new Error("access_request.action is required"); + if (!resource) throw new Error("access_request.resource is required"); + if (!requestedScope) throw new Error("access_request.requested_scope is required"); + if (!justification) throw new Error("access_request.justification is required"); + if (!policyId) throw new Error("policy.policy_id is required"); + if (!maxTtlMinutes || maxTtlMinutes < 1) throw new Error("policy.max_ttl_minutes must be a positive number"); + + return { + requestId, + subjectId, + requesterRole, + roles, + action, + resource, + requestedScope, + justification, + ticketId: stringValue(request.ticket_id), + requestedTtlMinutes: requestedTtlMinutes || maxTtlMinutes, + policyId, + maxTtlMinutes, + allowedRoles: isObject(policy.allowed_roles) ? policy.allowed_roles : {}, + deniedResources: arrayValue(policy.denied_resources).map(String), + sensitiveResources: arrayValue(policy.sensitive_resources).map(String), + requiredApprovals: isObject(policy.required_approvals) ? policy.required_approvals : {}, + grantDefaults: isObject(policy.grant_defaults) ? policy.grant_defaults : {}, + currentGrants, + }; +} + +function decide(input) { + const evidenceRefs = [ + `request:${input.requestId}`, + `policy:${input.policyId}`, + `entitlements:${input.subjectId}`, + ]; + const reasons = []; + + if (matchesAny(input.resource, input.deniedResources) || input.requestedScope.startsWith("secrets.")) { + return { + decision: "deny", + leastPrivilegeScope: null, + ttlMinutes: 0, + approvalGate: "not_applicable", + evidenceRefs, + reasons: ["requested resource or scope is explicitly denied by policy"], + }; + } + + const rolePolicy = firstRolePolicy(input.roles, input.allowedRoles); + if (!rolePolicy) { + return { + decision: "deny", + leastPrivilegeScope: null, + ttlMinutes: 0, + approvalGate: "not_applicable", + evidenceRefs, + reasons: ["requester has no role with access policy for this request"], + }; + } + + const allowedActions = arrayValue(rolePolicy.actions).map(String); + if (!allowedActions.includes(input.action)) { + return { + decision: "deny", + leastPrivilegeScope: null, + ttlMinutes: 0, + approvalGate: "not_applicable", + evidenceRefs, + reasons: [`action ${input.action} is not allowed for requester role`], + }; + } + + const allowedResources = arrayValue(rolePolicy.resources).map(String); + if (!matchesAny(input.resource, allowedResources)) { + return { + decision: "deny", + leastPrivilegeScope: null, + ttlMinutes: 0, + approvalGate: "not_applicable", + evidenceRefs, + reasons: ["requested resource does not match any allowed resource pattern"], + }; + } + + const allowedPrefixes = arrayValue(rolePolicy.scope_prefixes).map(String); + if (!allowedPrefixes.some((prefix) => input.requestedScope.startsWith(prefix))) { + return { + decision: "deny", + leastPrivilegeScope: null, + ttlMinutes: 0, + approvalGate: "not_applicable", + evidenceRefs, + reasons: ["requested scope is outside allowed scope prefixes"], + }; + } + + const leastPrivilegeScope = narrowScope(input.requestedScope, input.resource); + const ttlMinutes = Math.max(1, Math.min(input.requestedTtlMinutes, input.maxTtlMinutes)); + const isSensitive = matchesAny(input.resource, input.sensitiveResources); + const approvalGate = isSensitive + ? stringValue(input.requiredApprovals.sensitive_resource) || "human_approval_required" + : stringValue(input.grantDefaults.approval_gate) || "human_approval_required"; + + if (input.currentGrants.some((grant) => grant.scope === leastPrivilegeScope)) { + reasons.push("matching entitlement already exists; proposal keeps scope unchanged and flags duplicate grant risk"); + } else { + reasons.push("request matches allowed role, action, resource, and scope prefix"); + } + reasons.push(`ttl bounded to ${ttlMinutes} minutes by policy max ${input.maxTtlMinutes}`); + reasons.push("proposal is gated; no access is issued by this skill"); + + return { + decision: "grant", + leastPrivilegeScope, + ttlMinutes, + approvalGate, + evidenceRefs, + reasons, + }; +} + +function buildGrantProposal(input, decision) { + return { + schema: "runx.security.one_time_grant_proposal.v1", + proposal_id: `grant-proposal-${input.requestId}`, + subject_id: input.subjectId, + action: input.action, + resource: input.resource, + scope: decision.leastPrivilegeScope, + ttl_minutes: decision.ttlMinutes, + approval_gate: decision.approvalGate, + ticket_id: input.ticketId, + justification: input.justification, + policy_id: input.policyId, + issued_by_skill: false, + execution_status: "proposal_only", + handoff: { + catalog_skill: "least-privilege-auditor", + requires_human_approval: true, + }, + }; +} + +function buildEscalation(input, decision) { + if (decision.decision === "grant") { + return { + required: true, + lane: decision.approvalGate, + reason: "one-time grant proposal requires human approval before any access is issued", + ticket_id: input.ticketId || null, + }; + } + return { + required: decision.decision !== "deny", + lane: decision.decision === "deny" ? "not_applicable" : "human_review", + reason: decision.reasons[0] || "request did not satisfy policy", + ticket_id: input.ticketId || null, + }; +} + +function firstRolePolicy(roles, allowedRoles) { + for (const role of roles) { + if (isObject(allowedRoles[role])) return allowedRoles[role]; + } + return null; +} + +function narrowScope(scope, resource) { + if (!scope.endsWith("*")) return scope; + if (scope.endsWith("/*")) return scope.slice(0, -2); + const [prefix] = scope.split("*"); + const normalizedResource = resource.replace(/^\/+/, ""); + if (prefix.endsWith("/") && normalizedResource.startsWith(prefix.split(":").slice(1).join(":"))) { + return `${prefix}${normalizedResource.split("/").pop()}`; + } + return `${prefix}${normalizedResource}`; +} + +function matchesAny(value, patterns) { + return patterns.some((pattern) => matchesPattern(value, pattern)); +} + +function matchesPattern(value, pattern) { + if (!pattern) return false; + if (pattern === value) return true; + if (pattern.endsWith("*")) return value.startsWith(pattern.slice(0, -1)); + return false; +} + +function renderReport(packet, grantProposal) { + const lines = [ + "# access-request-review report", + "", + `Request: ${packet.request_id}`, + `Subject: ${packet.subject_id}`, + `Policy: ${packet.policy_id}`, + `Decision: ${packet.decision}`, + `Resource: ${packet.resource}`, + `Requested scope: ${packet.requested_scope}`, + `Least-privilege scope: ${packet.least_privilege_scope || "none"}`, + `TTL: ${packet.ttl_minutes} minutes`, + `Approval gate: ${packet.approval_gate}`, + `Escalation: ${packet.escalation.required ? packet.escalation.lane : "none"}`, + "", + "## Reasons", + ...packet.reasons.map((reason) => `- ${reason}`), + "", + grantProposal + ? `Grant proposal ${grantProposal.proposal_id} is proposal-only and requires ${grantProposal.approval_gate}.` + : "No grant proposal was emitted.", + "This skill does not mutate grants, move secrets, or call identity-provider APIs.", + ]; + return lines.join("\n"); +} + +function readInputs() { + if (process.env.RUNX_INPUTS_PATH) { + return JSON.parse(fs.readFileSync(process.env.RUNX_INPUTS_PATH, "utf8")); + } + if (process.env.RUNX_INPUTS_JSON) { + return JSON.parse(process.env.RUNX_INPUTS_JSON); + } + if (!process.stdin.isTTY) { + const raw = fs.readFileSync(0, "utf8").trim(); + if (raw) return JSON.parse(raw); + } + return {}; +} + +function requireObject(value, field) { + if (!isObject(value)) throw new Error(`${field} must be an object`); + return value; +} + +function isObject(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} + +function arrayValue(value) { + return Array.isArray(value) ? value : []; +} + +function stringValue(value) { + return typeof value === "string" && value.trim().length > 0 ? value.trim() : null; +} + +function numberValue(value) { + return typeof value === "number" && Number.isFinite(value) ? value : null; +} + +function sha256Json(value) { + return `sha256:${crypto.createHash("sha256").update(JSON.stringify(sortJson(value))).digest("hex")}`; +} + +function sortJson(value) { + if (Array.isArray(value)) return value.map(sortJson); + if (isObject(value)) { + return Object.fromEntries(Object.keys(value).sort().map((key) => [key, sortJson(value[key])])); + } + return value; +}