Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,10 @@ defaults are applied; output aliases such as `EvidenceRef` describe parsed data.
account, basis, and resource references.
- `hasna.capability_card.v1`: machine-readable description of a package command,
MCP tool, API operation, workflow, or agent skill.
- `hasna.provider_live_mode_standard.v1`: provider/live-mode standard with
canonical modes, capability cards, fail-closed credentials, no-side-effect
smokes, approval/idempotency/rollback/reconciliation gates, and first adopter
targets.
- `hasna.context_pack.v1`: bounded context bundle with objective, resources,
evidence, constraints, and token budget.
- `hasna.integration_ref.v1`: portable pointer to a project integration provider
Expand Down
90 changes: 90 additions & 0 deletions docs/PROVIDER-LIVE-MODE-STANDARD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Provider Live-Mode Safety Standard

Date: 2026-07-06

This standard applies to provider adapters in built or partially built `open-*`
and `iapp-*` apps. It excludes platform apps, empty apps, README-only apps,
scaffold-only apps, stale clone-only roots, and license-only repos.

Provider work must default to no side effects. Live mutation is allowed only
after sandbox proof, explicit operator approval, credential-reference checks,
idempotency, rollback or revocation evidence, and reconciliation evidence are
present.

## Canonical Provider Modes

- `mock`: deterministic fake implementation. No provider credentials and no
provider calls.
- `fixture`: recorded or local test data. No provider calls.
- `sandbox`: provider sandbox or test account. No production user, domain, DNS,
money, call, message, or filing side effects.
- `read_only_live`: live provider reads only. No mutation endpoints or outbound
delivery paths.
- `live_mutating`: live provider side effects. Requires all gates below.

Apps must expose provider mode in CLI JSON, MCP output, API response metadata,
health/readiness, audit/provenance, and operator evidence without printing
secret values.

## Required Capability Card

Every provider adapter must publish a `ProviderCapabilityCard` with:

- provider id, app id, adapter id, and owner package
- supported modes and default mode
- credential refs or lease refs required per mode
- operation cards with side-effect class and supported modes
- rate-limit and cost posture
- audit event names
- redaction rules
- evidence refs for validation, sandbox proof, and no-side-effect smokes

Raw provider secrets are not accepted in CLI args, MCP args, reports, logs, task
comments, committed config, or package examples. Missing or revoked credentials
must fail closed with a typed diagnostic.

## Live Mutation Gate

An operation may run in `live_mutating` only when all checks pass:

- requested mode is exactly `live_mutating`
- provider capability card allows the operation in `live_mutating`
- required credential refs or leases resolve and revocation checks pass
- approval record is approved, unexpired, and linked to the operation digest
- idempotency key is present
- sandbox evidence predates live execution
- rollback or revocation path is recorded
- reconciliation target is recorded

Environment flags alone must never enable live mutation. Fallback from live to
fixture/mock is forbidden for production commands because it can hide missing
authority or stale provider proof.

## Validation Gates

At minimum, adopters must run:

- native repo verification: `bun run verify` where available, otherwise
`bun run typecheck`, `bun test`, and `bun run build`
- provider conformance suite with no real side effects
- disabled-live smoke proving env flags alone cannot bypass gates
- secret-output scan over CLI, MCP, HTTP logs, reports, errors, and task evidence
- webhook/signature/replay fixtures where inbound providers are present

Live proof comes after sandbox proof. Live mutation additionally requires
operator approval and rollback or disable evidence.

## First Adoption Targets

- `open-mailery`: canonical `open-mailery`/`open-emails` boundary, local versus
self-hosted versus cloud auth model, Postgres/S3/SES readiness, signed
webhooks, and no-send/domain-change smokes.
- `open-telephony`: REST auth, Twilio signature and replay validation, toll
fraud controls, durable queues, retention, and opt-in sandbox/live provider
smokes.
- `open-feedback`: public submit separated from private read/export/triage,
scoped auth, rate/spam limits, durable store, dedupe, and signed forwarding
webhooks.

The machine-readable fixture is
`examples/provider-live-mode-standard.valid.json`.
56 changes: 56 additions & 0 deletions examples/provider-live-mode-standard.invalid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"schema": "hasna.provider_live_mode_standard.v1",
"id": "provider_live_mode_standard_invalid",
"createdAt": "2026-07-06T13:12:43.989Z",
"name": "Unsafe provider live-mode standard",
"version": "invalid",
"modes": ["fixture", "live_mutating"],
"requiredCapabilityFields": ["providerId"],
"liveMutationGate": {
"requiredMode": "live_mutating",
"requiredChecks": ["env flag is present"],
"forbiddenBypassSignals": ["missing approval id"],
"disabledLiveSmoke": "Not enough."
},
"noSideEffectSmoke": {
"requiredForModes": ["fixture"],
"commandEvidence": ["fixture command"]
},
"credentialPolicy": {
"acceptedInputs": ["credential_ref"],
"rawSecretInputsAllowed": false,
"missingCredentialBehavior": "fail_closed"
},
"operationCards": [
{
"providerId": "twilio",
"appId": "open-telephony",
"adapterId": "telephony-twilio",
"ownerPackage": "@hasna/telephony",
"modes": ["fixture", "live_mutating"],
"defaultMode": "fixture",
"credentialRequirements": [],
"operations": [
{
"operation": "send_sms",
"supportedModes": ["live_mutating"],
"sideEffectClass": "read_only",
"requiresApproval": false,
"requiresIdempotencyKey": false,
"requiresSandboxEvidence": false,
"requiresRollbackOrRevocation": false
}
],
"rateLimitPosture": "none"
}
],
"firstAdoptionTargets": [
{
"appId": "open-mailery",
"repo": "/home/hasna/workspace/hasna/opensource/open-mailery",
"priority": "p0",
"requiredEvidence": ["no-send smoke"],
"firstOperations": ["send_email"]
}
]
}
235 changes: 235 additions & 0 deletions examples/provider-live-mode-standard.valid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
{
"schema": "hasna.provider_live_mode_standard.v1",
"id": "provider_live_mode_standard_2026_07",
"createdAt": "2026-07-06T13:12:43.989Z",
"name": "Hasna provider/live-mode safety standard",
"version": "2026-07-06",
"modes": ["mock", "fixture", "sandbox", "read_only_live", "live_mutating"],
"requiredCapabilityFields": [
"providerId",
"appId",
"adapterId",
"ownerPackage",
"modes",
"defaultMode",
"credentialRequirements",
"operations",
"rateLimitPosture",
"auditEvents",
"redactionRules"
],
"liveMutationGate": {
"requiredMode": "live_mutating",
"requiredChecks": [
"capability card allows the operation",
"required credential refs or leases resolve",
"operator approval record is approved and unexpired",
"idempotency key is present",
"sandbox evidence predates live execution",
"rollback or revocation path is recorded",
"reconciliation target is recorded"
],
"forbiddenBypassSignals": [
"env flag alone",
"raw provider secret",
"missing approval id",
"missing idempotency key",
"implicit fallback from live to fixture"
],
"disabledLiveSmoke": "Run a live-disabled smoke proving provider mutation remains blocked even when provider-looking env vars are present."
},
"noSideEffectSmoke": {
"requiredForModes": ["mock", "fixture", "sandbox", "read_only_live"],
"commandEvidence": [
"CLI JSON output includes providerMode",
"MCP/API metadata includes providerMode",
"provider fixture proves no outbound send/call/domain/DNS/write happened"
],
"secretOutputScan": true
},
"credentialPolicy": {
"acceptedInputs": ["credential_ref", "lease_ref"],
"rawSecretInputsAllowed": false,
"missingCredentialBehavior": "fail_closed",
"revocationCheckRequired": true
},
"operationCards": [
{
"providerId": "ses",
"appId": "open-mailery",
"adapterId": "mailery-ses-outbound",
"ownerPackage": "@hasna/mailery",
"modes": ["fixture", "sandbox", "read_only_live", "live_mutating"],
"defaultMode": "fixture",
"credentialRequirements": [
{
"refName": "mailery.ses.send",
"requiredForModes": ["sandbox", "read_only_live", "live_mutating"],
"allowedSecretInputs": ["credential_ref", "lease_ref"],
"failClosedDiagnostic": "SES credential ref is required for provider health, sandbox, or live send.",
"revocationCheck": true
}
],
"operations": [
{
"operation": "send_email",
"supportedModes": ["fixture", "sandbox", "live_mutating"],
"sideEffectClass": "external_notification",
"requiresApproval": true,
"requiresIdempotencyKey": true,
"requiresSandboxEvidence": true,
"requiresRollbackOrRevocation": true,
"rollbackOrRevocation": "Disable provider credential lease and reconcile bounce/complaint or suppress recipient after send.",
"noSideEffectSmoke": "Fixture and sandbox send smokes must prove no production recipient delivery.",
"reconciliation": "Delivery event, bounce, complaint, and suppression records reconcile to message id."
},
{
"operation": "domain_readiness_check",
"supportedModes": ["fixture", "sandbox", "read_only_live"],
"sideEffectClass": "read_only",
"requiresApproval": false,
"requiresIdempotencyKey": false,
"requiresSandboxEvidence": false,
"requiresRollbackOrRevocation": false,
"noSideEffectSmoke": "DNS/domain readiness command must not change MX, DKIM, SPF, DMARC, route, or purchase state."
}
],
"rateLimitPosture": "Provider send limits and domain verification limits must be reported before enabling live mutation.",
"costPosture": "Live sends can incur provider delivery and storage costs.",
"auditEvents": ["provider.preflight", "provider.approval", "provider.execution", "provider.reconciliation"],
"redactionRules": ["recipient addresses are redacted in diagnostics", "credential refs are displayed without secret values"]
},
{
"providerId": "twilio",
"appId": "open-telephony",
"adapterId": "telephony-twilio",
"ownerPackage": "@hasna/telephony",
"modes": ["fixture", "sandbox", "read_only_live", "live_mutating"],
"defaultMode": "fixture",
"credentialRequirements": [
{
"refName": "telephony.twilio.account",
"requiredForModes": ["sandbox", "read_only_live", "live_mutating"],
"allowedSecretInputs": ["credential_ref", "lease_ref"],
"failClosedDiagnostic": "Twilio credential ref is required for webhook validation, sandbox, or live calls/messages.",
"revocationCheck": true
}
],
"operations": [
{
"operation": "send_sms_or_call",
"supportedModes": ["fixture", "sandbox", "live_mutating"],
"sideEffectClass": "bulk_message_or_call",
"requiresApproval": true,
"requiresIdempotencyKey": true,
"requiresSandboxEvidence": true,
"requiresRollbackOrRevocation": true,
"rollbackOrRevocation": "Emergency disable provider lease, block destination, stop queues, and reconcile provider status callbacks.",
"noSideEffectSmoke": "Fixture and sandbox smokes must prove no production SMS, WhatsApp, or voice call.",
"reconciliation": "Outbound job status reconciles with Twilio message/call/status callback identifiers."
},
{
"operation": "verify_inbound_webhook",
"supportedModes": ["fixture", "sandbox", "read_only_live"],
"sideEffectClass": "read_only",
"requiresApproval": false,
"requiresIdempotencyKey": false,
"requiresSandboxEvidence": false,
"requiresRollbackOrRevocation": false,
"noSideEffectSmoke": "Webhook fixture verifies signature, timestamp freshness, replay nonce, and expected URL without placing calls."
}
],
"rateLimitPosture": "Per-agent quotas, destination allow or deny rules, spend caps, and emergency disable are required.",
"costPosture": "Live messages, calls, number provisioning, and media can incur provider costs.",
"auditEvents": ["provider.preflight", "provider.approval", "provider.execution", "provider.reconciliation"],
"redactionRules": ["phone numbers are masked", "provider SIDs and webhook auth data are redacted"]
},
{
"providerId": "feedback-webhook",
"appId": "open-feedback",
"adapterId": "feedback-forwarder",
"ownerPackage": "@hasna/feedback",
"modes": ["mock", "fixture", "sandbox", "live_mutating"],
"defaultMode": "fixture",
"credentialRequirements": [
{
"refName": "feedback.webhook.signing",
"requiredForModes": ["sandbox", "live_mutating"],
"allowedSecretInputs": ["credential_ref", "lease_ref"],
"failClosedDiagnostic": "Webhook signing credential ref is required for sandbox or live forwarding.",
"revocationCheck": true
}
],
"operations": [
{
"operation": "forward_feedback",
"supportedModes": ["mock", "fixture", "sandbox", "live_mutating"],
"sideEffectClass": "external_notification",
"requiresApproval": true,
"requiresIdempotencyKey": true,
"requiresSandboxEvidence": true,
"requiresRollbackOrRevocation": true,
"rollbackOrRevocation": "Disable forwarding endpoint, dead-letter unconfirmed jobs, and revoke webhook signing lease.",
"noSideEffectSmoke": "Fixture forwarding writes only local delivery evidence and never calls external destinations.",
"reconciliation": "Forwarding job status reconciles to delivery receipt, retry, or dead-letter record."
}
],
"rateLimitPosture": "Submission rate limits, app quotas, retry leases, and dead-letter caps are required.",
"auditEvents": ["provider.preflight", "provider.approval", "provider.execution", "provider.reconciliation"],
"redactionRules": ["PII fields are classified before export", "webhook signing secrets are never emitted"]
}
],
"firstAdoptionTargets": [
{
"appId": "open-mailery",
"repo": "/home/hasna/workspace/hasna/opensource/open-mailery",
"priority": "p0",
"requiredEvidence": [
"canonical root and compatibility matrix",
"mode-specific health/readiness commands",
"no-send and no-domain-change smoke",
"signed webhook and replay fixtures"
],
"firstOperations": ["send_email", "domain_readiness_check"],
"blockedUntil": ["canonical open-mailery/open-emails root decision is recorded"]
},
{
"appId": "open-telephony",
"repo": "/home/hasna/workspace/hasna/opensource/open-telephony",
"priority": "p0",
"requiredEvidence": [
"REST auth negative tests",
"Twilio signature and replay fixtures",
"quota and spend-cap fixtures",
"opt-in sandbox smoke"
],
"firstOperations": ["send_sms_or_call", "verify_inbound_webhook"]
},
{
"appId": "open-feedback",
"repo": "/home/hasna/workspace/hasna/opensource/open-feedback",
"priority": "p0",
"requiredEvidence": [
"public submit versus private read/export auth matrix",
"rate-limit and spam fixtures",
"durable store and dedupe fixtures",
"signed webhook delivery fixture"
],
"firstOperations": ["forward_feedback"]
}
],
"evidenceRefs": [
{
"id": "reviewer_11_provider_deploy",
"kind": "report",
"uri": "repo://reports/task-proposals/adversarial-12/reviewer-11-provider-deploy.md",
"summary": "Provider/deployment reviewer requirements for capability cards, live modes, approval, and reconciliation."
},
{
"id": "reviewer_01_open_finance_comms",
"kind": "report",
"uri": "repo://reports/task-proposals/adversarial-12/reviewer-01-open-finance-comms.md",
"summary": "OSS finance/comms task proposals for mailery, telephony, and feedback first adopters."
}
]
}
Loading
Loading