Sandbox Adapter Kit turns the local Sandbox Conformance Harness into an integration contract that another team can implement, test, and publish to the Witnora Hosted Control Plane without granting production write access.
External teams should start with the single-package onboarding path:
npx witnora@latest sandbox init
npx witnora@latest sandbox certify --adapter ./witnora.sandbox.mjs
This writes one dependency-free adapter template and runs the v0.2 conformance
suite bundled inside agentcert. It does not require the unpublished
@agentcert/onegent-runtime package or an Witnora source checkout.
npm run onegent:sandbox-conformance
Output:
.onegent/sandbox-conformance/sandbox-adapter-conformance.json
A passing report uses
schemaVersion: "agentcert.sandbox_adapter_conformance.v0.2". The package
ships sandbox-adapter-conformance.schema.json for independent validation.
Use createSandboxSystemAdapter() so the safety declaration and synthetic seed
validation cannot be accidentally weakened:
const system = createSandboxSystemAdapter({
name: "customer-sandbox",
allowedTargetSystems: ["CustomerSandboxCRM"],
handlers: {
createTenant,
deleteTenant,
resetTenant,
seedTenant,
hasTenant,
snapshotTenant,
adapterForTenant,
},
});
const report = await runSandboxAdapterConformanceSuite({ system });
The runnable template is
examples/onegent/sandbox-system-adapter-template.mjs.
The suite actively checks the adapter contract, all ten v0.1 safety controls,
the full tenant lifecycle, and temporary-tenant cleanup.
Every harness tenant receives a one-hour lease by default. The maximum default lease is 24 hours. Services can shorten these values, renew an active lease, run deterministic cleanup, or close the harness and delete all remaining tenants:
const harness = createSandboxCertificationHarness({
system,
tenantTtlMs: 15 * 60_000,
maxTenantTtlMs: 60 * 60_000,
});
const lease = await harness.createTenant({ id: "pilot-42", synthetic: true });
await harness.renewTenant(lease.tenantId, 10 * 60_000);
await harness.cleanupExpiredTenants();
await harness.close();
The in-process timer is a safety net, not a distributed scheduler. A hosted adapter should also enforce expiry in its own durable store so cleanup survives process restarts.
createStripeTestModeReadOnlyAdapter() demonstrates the first vendor boundary.
It:
rk_test_ restricted test-mode key;https://api.stripe.com;livemode is explicitly false;client_secret or metadata.const stripe = createStripeTestModeReadOnlyAdapter({
restrictedApiKey: process.env.STRIPE_RESTRICTED_TEST_KEY!,
});
const snapshot = await stripe.retrievePaymentIntent("pi_...");
This adapter does not create, confirm, capture, cancel, or refund payments. The
restricted key must grant only the Stripe read permission required by the
selected method. See Stripe’s official API key guidance
and PaymentIntent retrieval API.
The public npx witnora@latest sandbox stripe-readonly workflow and its evidence
boundary are documented in
Bounded Vendor Sandbox Egress v0.4.
The CLI can create a hosted run, upload a manifest-complete evidence bundle, and complete the run:
export WITNORA_PROJECT_ID="your-project-id"
export WITNORA_API_KEY="your-scoped-api-key"
export WITNORA_BASE_URL="https://witnora.com"
onegent-runtime sandbox-conformance --push
For external users, the supported public command is:
npx witnora@latest onboard --project your-project-id
npx witnora@latest sandbox push --adapter ./witnora.sandbox.mjs
The API key needs runs:write and evidence:write. The key is read only from
the environment and is never accepted as a CLI flag or written into evidence.
Programmatic callers can use uploadSandboxCertificationReport() with an
injected fetch implementation.
The intended progression remains: local synthetic adapter, official vendor test mode read-only, official vendor sandbox with narrowly approved writes, production read-only shadow, then a manually approved low-limit canary.