Skip to main content

The app conformance suite

apps/api/src/modules/apps/conformance/sample-app.conformance.spec.ts checks that an app built with @arc/app-kit works with ARC: it uses the API's own signers and rules, not copies of them, so a change on either side that breaks apps fails here.

The sample app is what a developer gets: arc app init scaffolds it, and the kit's createArcAppHandler serves it on 127.0.0.1 with test-mode events only, as arc app dev does.

CheckAgainst
The scaffolded manifest is validThe registry's parseAppManifest
Webhook HMAC is verified: a signed body is taken, including during a secret rotation; a tampered body, a foreign secret and a stale timestamp are refused; a retried event is handled oncesignWebhookBodyWithSecrets and buildOutboxWebhookBody, with the headers webhook-delivery.service.ts sends
Session tokens are verified: a minted token is taken once; another app's token, a foreign secret and alg: none are refusedsignAppJwt
The test-mode header is honoured: a dev tenant's and a test-mode organization's events say test and are taken; a live organization's are refusedarcEnvironment

Running it​

It is skipped unless armed, like the MCP conformance harness:

ARC_APP_CONFORMANCE=1 pnpm --filter @ims/api test src/modules/apps/conformance

The arming rails themselves always run: they check the suite stays skipped unless ARC_APP_CONFORMANCE is exactly 1, and refuses a live leg against anything but loopback.

The live leg (your own booted API)​

Add these to also run arc app dev and arc app deploy against an API on this machine:

VariableValue
ARC_APP_CONFORMANCE_API_URLhttp://127.0.0.1:<port> — loopback only; anything else is refused before the suite starts
ARC_TOKENA staff access token from that API, for a user with settings:apps:manage in an org with the Custom Apps plan switch
ARC_APP_IDA private app in that org

Private apps need the Custom Apps plan switch, which no stock plan includes yet, so today the live leg can only pass against an API whose plan entitles the org to customApps.

The live leg publishes a new version of that app (numbered 0.1.<unix time>, not made current) on every run. Run it only against your own throwaway database, never a shared one.