auth key fills all three identity seams createVendo needs: the
request-to-principal resolver, the away/MCP actAs seam, and the MCP door’s
OAuth adapter. vendo init --auth <preset> answers the auth question
non-interactively. The presets are the zero-argument exports of
@vendoai/vendo/server: authJs(), clerk(), supabase(), auth0(), plus
jwt({ secret }) and none.
Detect which one the host uses
Init detects frompackage.json (dependencies and devDependencies):
Cross-check with env and file signals before passing
--auth:
- authJs:
AUTH_SECRETin.env*, anauth.ts/auth.config.ts, or anapp/api/auth/[...nextauth]route. - clerk:
CLERK_SECRET_KEY/NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYin.env*,<ClerkProvider>in the layout,clerkMiddlewareinmiddleware.ts. - supabase:
SUPABASE_URL/NEXT_PUBLIC_SUPABASE_URLorSUPABASE_JWT_SECRETin.env*, alib/supabase*client module. - auth0:
AUTH0_DOMAINorAUTH0_ISSUER_BASE_URLin.env*. - jwt: none of the above, but the host’s API verifies its own
Authorization: BearerHS256 tokens with a shared secret.
--auth is that confirmation). Zero or several: init stays anonymous and
prints one advisory line naming the exact line to add later; see “when to
ask” below.
What init scaffolds vs what you hand-wire
When init creates the composition (the Next catch-all route orvendo/server.ts on Express), --auth <preset> writes the auth: <preset>() line into it. On a re-run against an existing composition, init
changes nothing about auth; you add the one line yourself.
--auth authJs|clerk|supabase|auth0with the SDK detected: fully wired, nothing stubbed.- Same flag without the SDK in
package.json: wired, plus a stub: install the runtime package from the table above (npm install @auth/core, etc.) before the first authenticated run. The preset fails loud until then, and the agent tail repeats this. --auth jwt: nothing is wired.jwt()cannot be zero-argument (no vendor env var exists for a host-owned scheme); init prints the recipe and you addauth: jwt({ secret: () => process.env.YOUR_SIGNING_SECRET })by hand.--auth none: the composition getsprincipal: async () => null; every session is anonymous until a preset line replaces it.
Per-preset wiring
Every preset is zero-argument in the standard case: it reads its provider’s env variable and derives the principal’s display from session-token claims. Verify the env variable exists; that is the whole hand-wiring for present calls.
Notes that matter in practice:
- supabase: never use the anon key or a service-role token as the secret;
they are complete tokens, not the signing secret. Hosted projects on newer
signing keys use ES256; setting
SUPABASE_URLis enough for that path (the preset verifies against<url>/auth/v1/.well-known/jwks.json, viajose). - clerk / auth0 away execution: the provider holds the session signing
keys, so away (user-not-present) calls use a host-owned
VendoAwaytoken instead. That needsVENDO_AWAY_TOKEN_SECRET(generate withopenssl rand -base64 32; ask your human before creating secrets) set for both the Vendo runtime and the host API, plus the preset’s verifier middleware on the host API. Present calls work without any of this; doctor reports the gap asE-AUTH-007/E-AUTH-004only when away wiring is expected. Full recipes: actAs presets. - Custom identity mapping: every preset accepts
{ secret, user }options;user: (subject, claims) => ({ display, email }) | nullresolves identity from the host’s own user table and returningnullmeans “subject unknown”, failing closed.
When to ask the human
Relay the decision instead of guessing when:- Several providers are detected (for example
next-authand@supabase/*both present; one may be auth, the other just a database client). Name what you found and ask which one signs the app’s sessions. - No provider is detected but the app clearly has login. Ask whether it is
a host-owned JWT scheme (
--auth jwt+ the recipe) or something Vendo has no preset for (hand-wire theprincipal/actAs/oauthtrio per actAs presets). - A secret has to be created (
VENDO_AWAY_TOKEN_SECRET, ajwtsigning secret): confirm before generating and never invent values for provider keys. - Anonymous is a real option.
--auth noneis a valid ship for products without login; confirm the host actually has no user identity before settling for it.
vendo doctor --json probes both seams live: present
credentials (E-AUTH-001..003) and the actAs mint + verify round-trip
(E-AUTH-004..007). See verify.