everyapp 0.3.0-rc.2 → 0.3.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/README.md +1 -1
  2. package/dist/access-YWM4BSBD.js +1 -0
  3. package/dist/activity-RTCAUMQS.js +14 -0
  4. package/dist/ai-UOYQFTAX.js +5 -0
  5. package/dist/apps-PEOBCDNU.js +14 -0
  6. package/dist/chunk-26KS6TD6.js +1 -0
  7. package/dist/chunk-3EADVHFN.js +15 -0
  8. package/dist/chunk-4LKLQOM7.js +7 -0
  9. package/dist/{chunk-PNLU5U3P.js → chunk-5IC3ZN52.js} +1 -1
  10. package/dist/{chunk-H7PDL3X5.js → chunk-AMCNAKYU.js} +1 -1
  11. package/dist/{chunk-23HJ5I2A.js → chunk-ASCNE7GS.js} +1 -1
  12. package/dist/{chunk-6OGVO7QK.js → chunk-BCTEWNN4.js} +1 -1
  13. package/dist/{chunk-W3JCQHHU.js → chunk-BK4NK34C.js} +1 -1
  14. package/dist/chunk-BWNZ6F4F.js +2 -0
  15. package/dist/chunk-DCFHBCCW.js +9 -0
  16. package/dist/{chunk-2Y5Q6A5K.js → chunk-DWTJ4OLZ.js} +1 -1
  17. package/dist/chunk-EP3IQ354.js +20 -0
  18. package/dist/{chunk-OP4DYXYI.js → chunk-EP4HRYXP.js} +1 -1
  19. package/dist/{chunk-HOUHG236.js → chunk-EQLY5I46.js} +1 -1
  20. package/dist/{chunk-YHJZMJYZ.js → chunk-F5XK23HV.js} +1 -1
  21. package/dist/{chunk-5PTNYZK6.js → chunk-FB2OC32C.js} +3 -3
  22. package/dist/chunk-FQ2A7YAP.js +57 -0
  23. package/dist/{chunk-OM2HDWVP.js → chunk-GAPQSINH.js} +1 -1
  24. package/dist/chunk-GJK6GAWO.js +1 -0
  25. package/dist/chunk-H4EZTXIK.js +1 -0
  26. package/dist/chunk-HJGZGZFE.js +32 -0
  27. package/dist/{chunk-CCWBPAKH.js → chunk-I7FNTTUY.js} +3 -3
  28. package/dist/chunk-KOBKGQ27.js +91 -0
  29. package/dist/{chunk-PS7S74DG.js → chunk-KQM6ZUX5.js} +1 -1
  30. package/dist/chunk-KVCL64LI.js +1 -0
  31. package/dist/{chunk-4JRIS6S6.js → chunk-LEEUODYR.js} +1 -1
  32. package/dist/{chunk-6PGUCTJ6.js → chunk-LI3YLKLC.js} +1 -1
  33. package/dist/chunk-MEIWV7FJ.js +20 -0
  34. package/dist/{configure-F7UZCJUG.js → chunk-MLTNW3AM.js} +2 -2
  35. package/dist/chunk-NDOEON54.js +4 -0
  36. package/dist/chunk-NW7SJPSP.js +37 -0
  37. package/dist/chunk-O64FPLGX.js +1 -0
  38. package/dist/chunk-ORGV2NNT.js +8 -0
  39. package/dist/chunk-PDPUGYJW.js +47 -0
  40. package/dist/chunk-QFTBDXLF.js +146 -0
  41. package/dist/{chunk-E3EPK27P.js → chunk-QJRZFIYC.js} +2 -2
  42. package/dist/chunk-QKHLRLK3.js +15 -0
  43. package/dist/{chunk-CZXHKPRZ.js → chunk-R2DF3ENZ.js} +1 -1
  44. package/dist/{chunk-IUOBIB5M.js → chunk-R7JOHK7Y.js} +2 -2
  45. package/dist/chunk-RL72BUT4.js +1 -0
  46. package/dist/chunk-RQBSTNL7.js +1 -0
  47. package/dist/{chunk-H6QQMY7B.js → chunk-S2G4G73L.js} +1 -1
  48. package/dist/{chunk-LXYMDZER.js → chunk-SGRGD7L4.js} +1 -1
  49. package/dist/{chunk-T2GD7RHS.js → chunk-TIYSZWP5.js} +1 -1
  50. package/dist/chunk-TUXKBLLJ.js +10 -0
  51. package/dist/chunk-U4ATZJEU.js +6 -0
  52. package/dist/chunk-V7BWZOSZ.js +26 -0
  53. package/dist/{chunk-J35DDCNU.js → chunk-VVKQJSF3.js} +1 -1
  54. package/dist/chunk-WVHJIYQ3.js +14 -0
  55. package/dist/{chunk-ZEXVHB22.js → chunk-XBD7PQN5.js} +1 -1
  56. package/dist/{chunk-N5GQLGEC.js → chunk-YAZHTVUV.js} +1 -1
  57. package/dist/configure-CAWCTPYY.js +1 -0
  58. package/dist/{create-2E5HVWYK.js → create-3ZWJQXPK.js} +2 -2
  59. package/dist/{db-HEQVTDTM.js → db-B7KO37SV.js} +1 -1
  60. package/dist/delete-CB3EPOU4.js +3 -0
  61. package/dist/deploy-5O7LV5OX.js +1 -0
  62. package/dist/dev-4JYCVVOZ.js +86 -0
  63. package/dist/{email-RLZFUFUN.js → email-SWJQXEUR.js} +1 -1
  64. package/dist/{events-2V5VRBWP.js → events-GJV2KVIU.js} +13 -13
  65. package/dist/{groups-HDG5DVFY.js → groups-NNTLSXMK.js} +1 -1
  66. package/dist/index.js +104 -95
  67. package/dist/install-UAG3CNYW.js +1 -0
  68. package/dist/{login-KHJBC64Z.js → login-QEKMLMUB.js} +1 -1
  69. package/dist/mcp-AFGVOM5D.js +1 -0
  70. package/dist/provider-622VM7UZ.js +29 -0
  71. package/dist/release-CXEX6O7F.js +1 -0
  72. package/dist/{restore-FFGYV7NO.js → restore-PGIVEA5P.js} +1 -1
  73. package/dist/secret-3LUDEDUW.js +1 -0
  74. package/dist/setup-PUD375AM.js +1 -0
  75. package/dist/{signin-AP3D4XM4.js → signin-EP5JLAT5.js} +2 -2
  76. package/dist/status-LK6DNLCI.js +5 -0
  77. package/dist/teardown-6RURRSME.js +22 -0
  78. package/dist/update-YJFTOZEV.js +1 -0
  79. package/dist/{workspace-CJC5I66L.js → workspace-VW7ZZRFP.js} +3 -3
  80. package/package.json +5 -5
  81. package/skills/build-every-app/SKILL.md +22 -13
  82. package/skills/build-every-app/patterns.md +16 -3
  83. package/skills/everyapp/SKILL.md +33 -18
  84. package/skills/everyapp/mcp.md +22 -8
  85. package/skills/everyapp/security-review.md +1 -1
  86. package/skills/everyapp/setup/README.md +18 -1
  87. package/skills/everyapp/setup/access.md +11 -7
  88. package/skills/everyapp/setup/ai-gateway.md +117 -0
  89. package/skills/everyapp/setup/ai.md +139 -0
  90. package/skills/everyapp/setup/context.md +2 -2
  91. package/skills/everyapp/setup/dataforseo.md +3 -3
  92. package/skills/everyapp/setup/email.md +1 -1
  93. package/skills/everyapp/setup/gateway.md +3 -3
  94. package/skills/everyapp/setup/google.md +8 -6
  95. package/skills/everyapp/setup/microsoft-365.md +4 -2
  96. package/skills/everyapp/setup/providers.md +27 -9
  97. package/skills/everyapp/setup/remote-mcp.md +1 -1
  98. package/skills/everyapp/setup/signin-apple.md +3 -3
  99. package/skills/everyapp/setup/signin-cloudflare.md +1 -1
  100. package/skills/everyapp/setup/signin-github.md +1 -1
  101. package/skills/everyapp/setup/signin-google.md +2 -2
  102. package/skills/everyapp/setup/signin-microsoft.md +1 -1
  103. package/skills/everyapp/setup/slack.md +2 -0
  104. package/dist/access-LGEN3ABJ.js +0 -1
  105. package/dist/activity-RSD4D66F.js +0 -12
  106. package/dist/apps-4RCFXT74.js +0 -13
  107. package/dist/chunk-AGTCKFRB.js +0 -22
  108. package/dist/chunk-BHMRY53V.js +0 -2
  109. package/dist/chunk-CB4U7H2G.js +0 -4
  110. package/dist/chunk-CZTEDWZ2.js +0 -22
  111. package/dist/chunk-EGEWRFVY.js +0 -1
  112. package/dist/chunk-ELK7QZ2M.js +0 -1
  113. package/dist/chunk-FJJSSVET.js +0 -33
  114. package/dist/chunk-GKBPYVP3.js +0 -25
  115. package/dist/chunk-IFCPALVC.js +0 -46
  116. package/dist/chunk-J455FAEN.js +0 -7
  117. package/dist/chunk-JYKRUS5C.js +0 -1
  118. package/dist/chunk-LYNBZ7RV.js +0 -57
  119. package/dist/chunk-M2QQ2UD7.js +0 -132
  120. package/dist/chunk-NFU4YFXB.js +0 -1
  121. package/dist/chunk-ONLNJXVQ.js +0 -32
  122. package/dist/chunk-RMF4YGD6.js +0 -98
  123. package/dist/chunk-TLRM3P7V.js +0 -15
  124. package/dist/chunk-WOVS3GV5.js +0 -10
  125. package/dist/chunk-ZECBN24L.js +0 -15
  126. package/dist/chunk-ZFCUH5PK.js +0 -7
  127. package/dist/delete-XCSIQIRS.js +0 -3
  128. package/dist/deploy-3QOZEZHC.js +0 -1
  129. package/dist/dev-JL3ZP5BT.js +0 -86
  130. package/dist/install-Z5FYXUYM.js +0 -1
  131. package/dist/mcp-EFDUKJ5V.js +0 -1
  132. package/dist/provider-EOI5PTFH.js +0 -13
  133. package/dist/release-74J5ZIL3.js +0 -1
  134. package/dist/secret-7FKKTLHB.js +0 -1
  135. package/dist/setup-PAKRCAFO.js +0 -1
  136. package/dist/status-RMWNHOEK.js +0 -4
  137. package/dist/teardown-E4JNKAZJ.js +0 -22
  138. package/dist/update-DWVZFNPS.js +0 -1
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: everyapp
3
- description: Create, run, deploy and manage private web apps on Cloudflare with everyapp (Every App). Use before running any everyapp command, to deploy an app, set a secret, query its database, check the Gateway, or answer any "how do I" question about Every App.
3
+ description: Create, run, deploy and manage private web apps on Cloudflare with everyapp (EveryApp). Use before running any everyapp command, to deploy an app, set a secret, query its database, check the Gateway, or answer any "how do I" question about EveryApp.
4
4
  ---
5
5
 
6
6
  everyapp: self-hosted platform for private web apps on Cloudflare. While
7
7
  publishing is paused the command is `pnpm everyapp` (never `npx everyapp`),
8
- run from the Every App checkout or from an app's directory (its package.json
8
+ run from the EveryApp checkout or from an app's directory (its package.json
9
9
  links the CLI).
10
10
 
11
11
  Docs: two worlds. The user docs (https://everyapp.com/docs) are short and
@@ -13,7 +13,9 @@ for the person. The agent docs are yours: https://everyapp.com/llms.txt
13
13
  lists every page with a one-line summary; a page is Markdown at its URL
14
14
  plus .md (https://everyapp.com/docs/agent/manifest.md); in a checkout they
15
15
  are landing-page/content/agent/*.md. Pages: setup (driving setup for a
16
- person, every handoff), cli (every command and flag), json (the --json
16
+ person, every handoff), apps (the open apps: each one's id, repository,
17
+ install command, every setting and whether it needs R2), cli (every
18
+ command and flag), json (the --json
17
19
  protocol), errors (every code and its fix), troubleshooting (by symptom),
18
20
  status (the report, every line, and its fixes in order), manifest
19
21
  (everyapp.config.ts), identity (headers, token, env, SDK), secrets
@@ -21,7 +23,11 @@ status (the report, every line, and its fixes in order), manifest
21
23
  email, events (webhooks, email alerts, the signature, every event type,
22
24
  emit()), mcp (tools, connector, OAuth), deploy (the pipeline, what the policy
23
25
  check refuses), providers (API providers: the catalog, registering one,
24
- a spec's fields), access (Cloudflare Access), workspace (the workspace from
26
+ a spec's fields), access (Cloudflare Access), signin (sign-in providers:
27
+ Continue with Google, Microsoft, GitHub, Apple, Cloudflare or SSO; every
28
+ setting, the signin commands), ai (the Gateway's default model
29
+ provider: the order, `ai default|show|list`, AI Gateway in front, what an
30
+ app writes), workspace (the workspace from
25
31
  the terminal and from an AI client: every command and every tool), gateway
26
32
  (everything a Gateway offers: apps vs MCP apps vs the compact MCP surface,
27
33
  people, groups and caps, the CLI by job, what to suggest next, other MCP
@@ -29,8 +35,10 @@ servers), build (the build-every-app skill as a page), patterns (its
29
35
  example-patterns index), repo/* (security model, capabilities, runbooks).
30
36
  Step-by-step setup of each
31
37
  integration (the Gateway, Access, email, DataForSEO, Context, OpenRouter,
32
- OpenAI, Workers AI, Google, Microsoft 365, Slack; every catalog provider by
33
- id in setup/providers.md): setup/README.md beside this file.
38
+ OpenAI, Workers AI, the default model provider (setup/ai.md) and Cloudflare
39
+ AI Gateway (setup/ai-gateway.md), Google, Microsoft 365, Slack; every
40
+ catalog provider by id in setup/providers.md): setup/README.md beside this
41
+ file.
34
42
  Answering "how do I": fetch the page for the subject and answer from it
35
43
  with the exact command or manifest field, naming the page. A failed
36
44
  command: look its code up on errors (anchor: the code, lowercased) before
@@ -55,8 +63,8 @@ scope sets (google-workspace, microsoft-365, slack) is listed with
55
63
  scopes: [...] and held to them; after a schema change run pnpm run
56
64
  db:generate; a value only dev needs goes in .dev.vars, never in git (dev
57
65
  page). The way back: the first row of the app's sidebar (or of its page
58
- header when it has no sidebar) is a muted "‹ Every App" link to
59
- GATEWAY_URL (aria-label "Back to Every App"), above the app's own name,
66
+ header when it has no sidebar) is a muted "‹ EveryApp" link to
67
+ GATEWAY_URL (aria-label "Back to EveryApp"), above the app's own name,
60
68
  so every app on the Gateway reads the same; put nothing else about the
61
69
  Gateway in the footer.
62
70
 
@@ -107,11 +115,15 @@ Data: pnpm everyapp db migrate (local) pnpm everyapp db query "<sql>" [--lo
107
115
  (a production write stops with CONFIRMATION_REQUIRED: add --yes only
108
116
  if the user said so). pnpm everyapp db restore --json undoes the last
109
117
  deploy's migrations (and every write since): only when the user asks.
110
- Postgres: an app with resources.hyperdrive or migrations.dialect "postgresql"
111
- reads POSTGRES_DATABASE_URL (and <BINDING>_ORIGIN_URL) from
112
- .everyapp/secrets.env, which the user writes; never print, commit or
113
- ask for its values in chat. deploy runs drizzle-kit migrate before
114
- the upload.
118
+ Postgres: any Postgres with a URL (Neon, Supabase, …) goes through Hyperdrive:
119
+ resources.hyperdrive, database.provider "postgres", migrations.dialect
120
+ "postgresql" (manifest page, #postgres). The deploy reads
121
+ POSTGRES_DATABASE_URL (and <BINDING>_ORIGIN_URL) from
122
+ .everyapp/secrets.env, which the user writes (configure doesn't);
123
+ never print, commit or ask for its values in chat. Without it deploy
124
+ stops HYPERDRIVE_ORIGIN_MISSING (data.variables, data.file) and status
125
+ names it under the app. deploy runs drizzle-kit migrate before the
126
+ upload. Turso is libSQL: app secrets, not Hyperdrive.
115
127
  Secrets: pnpm everyapp configure <app> --json sets up an app: every setting its
116
128
  manifest declares, each where it is held (an AI key or a capability's
117
129
  client on the Gateway, once for every app; the app's own secrets on
@@ -248,7 +260,7 @@ Tools: an app's AI tools are defineTools() in src/tools.ts; the Gateway
248
260
  http://<app-id>.localhost:8787/mcp) and snapshots them at deploy.
249
261
  How to write, connect and debug them: mcp.md beside this file.
250
262
  Wipe: pnpm everyapp teardown --account-id <id> --dry-run --json deletes
251
- EVERYTHING Every App made in one Cloudflare account (the Gateway too):
263
+ EVERYTHING EveryApp made in one Cloudflare account (the Gateway too):
252
264
  only on a dedicated test account the user names, never one they use.
253
265
  Always --dry-run --json first and show the user data.plan. It needs
254
266
  EVERYAPP_TEST_ACCOUNTS=<id> and CLOUDFLARE_API_TOKEN scoped to that
@@ -270,16 +282,19 @@ Security: the Gateway signs people in and passes identity to the app in a
270
282
  against the list there. No CI job and no API key: the review runs
271
283
  where you run. Fix what it finds, or say in the pull request why a
272
284
  finding is a false positive. What the Gateway promises and what an
273
- app must do itself: docs/threat-model.md in the Every App checkout
285
+ app must do itself: docs/threat-model.md in the EveryApp checkout
274
286
  (https://everyapp.com/docs/agent/repo/threat-model).
275
287
 
276
288
  Only a person can: approve the Cloudflare login, open the owner link, pick
277
289
  a workers.dev subdomain, pay for Workers Paid, buy a domain. Interactive
278
290
  setup optionally offers browser deployment: a Yes/No prompt (default No),
279
291
  then a hidden Cloudflare API token prompt. Never ask for its value in chat.
280
- It then offers Cloudflare Access (Yes by default; the team name asked for
281
- when the account has none); under --json neither is asked, and --yes
282
- answers No to both.
292
+ It then puts Cloudflare Access in front by default (asked at a terminal,
293
+ Yes by default; the team name asked for when the account has none); under
294
+ --json or --yes it goes on without a question when the account can (a Zero
295
+ Trust team exists, the Cloudflare login is an owner or admin), else the
296
+ warning ACCESS_SETUP_SKIPPED says why and names access on; --no-access
297
+ leaves it off. --yes answers No to browser deployment.
283
298
  Handing off: stop, tell the person in one or two lines what to do (the
284
299
  action's url and code, what to click, what to come back with), and resume
285
300
  with data.rerun when they say it is done. Two more only a person can do:
@@ -159,8 +159,10 @@ Workspace tools (Gateway connector only; every admin action of the UI, listed
159
159
  `everyapp_workspace_apps`, `everyapp_workspace_app_set`,
160
160
  `everyapp_workspace_app_access`, `everyapp_workspace_app_access_set`,
161
161
  `everyapp_workspace_app_cap`, `everyapp_workspace_cap_default`,
162
- `everyapp_workspace_usage`,
163
- `everyapp_workspace_activity`, `everyapp_workspace_rename`, and the
162
+ `everyapp_ai_show` (any member), `everyapp_ai_default`,
163
+ `everyapp_workspace_usage`, `everyapp_workspace_activity`,
164
+ `everyapp_workspace_audit` (the audit log: who did what, 90 days),
165
+ `everyapp_workspace_rename`, and the
164
166
  events tools `everyapp_workspace_events`, `everyapp_workspace_events_add`
165
167
  (a generated signing secret comes back once, in that answer: it opens
166
168
  nothing on the Gateway, so the person stores it in the receiver now),
@@ -190,8 +192,17 @@ Provider tools (Gateway connector only; one audit row per call like the
190
192
  never a replace of a row that is there) and
191
193
  `everyapp_provider_remove` (`{ id }`: refuses a built-in, a row
192
194
  whose key is set, and a row an app lists). A hosted Gateway hides
193
- both writers. No key is ever set through a tool: `everyapp secret
194
- set NAME` from the terminal. Full reference:
195
+ both writers. `everyapp_provider_add` also takes `{ preset }`, an
196
+ OAuth preset (github-oauth, notion-oauth, …; the list tool's
197
+ `presets`) or a built-in client (google-workspace), and answers
198
+ the redirect URI to register at the provider's console. Every
199
+ member gets `everyapp_provider_connections` (their own connected
200
+ accounts; `all: true` the organization's, owners and admins) and
201
+ `everyapp_provider_disconnect` (their own; owners and admins
202
+ anyone's or the organization's). Connecting is a browser flow at
203
+ `<gateway>/connect/<provider>`, never a tool. No key, client or
204
+ token ever passes through a tool: `everyapp secret set NAME` from
205
+ the terminal, or Admin → Providers. Full reference:
195
206
  /docs/agent/mcp#provider-tools.
196
207
  Sign-in provider tools (Gateway connector only, owners and admins, reads):
197
208
  `everyapp_signin_list` (Manage workspace → Sign-in: each sign-in
@@ -264,8 +275,11 @@ snapshot yet shows no hint (its tools load on first use).
264
275
  Scripts and anything that takes a header: a personal access token. In the
265
276
  Gateway open **Admin** (the sidebar footer's **Manage workspace**; owners and
266
277
  admins only), tab **Tokens**, heading **Personal access tokens**, button
267
- **New token**: a name, **All my apps** or one app, an expiry (default
268
- 90 days). The token is `epat_…`, shown once, read & write. Send it as
278
+ **New token**: a name, an access level (**Read only**, the default:
279
+ `mcp:read`; **Read & write**: `mcp:write`; **Full access**: `*`, with a
280
+ warning), **All my apps** or one app (the one app is the default when there
281
+ is one), an expiry (default 30 days). The token is `epat_…`, shown once.
282
+ Send it as
269
283
  `Authorization: Bearer epat_…` to `https://<gateway>/mcp` (a token made for one
270
284
  app sees that app alone):
271
285
 
@@ -284,7 +298,7 @@ deactivating a person ends all of theirs; tokens are revoked on Admin, Tokens.
284
298
 
285
299
  Other servers worth having beside the Gateway's (Cloudflare's API server for
286
300
  the owner, read before write; nothing else): [setup/gateway.md](setup/gateway.md),
287
- "Optional: MCP servers worth having beside Every App". Offer one in a line;
301
+ "Optional: MCP servers worth having beside EveryApp". Offer one in a line;
288
302
  never add it unasked.
289
303
 
290
304
  ## Troubleshooting
@@ -310,7 +324,7 @@ Admin (a connection made with **Only these apps** needs a reconnect to add one).
310
324
  expired or revoked token, a bearer that isn't `epat_` on an app host, the token
311
325
  of a revoked connection); its body depends on the surface. On
312
326
  `https://<gateway>/mcp` with OAuth on: an empty body and
313
- `WWW-Authenticate: Bearer realm="OAuth", resource_metadata="…/.well-known/oauth-protected-resource/mcp", scope="mcp:read"`
327
+ `WWW-Authenticate: Bearer realm="OAuth", resource_metadata="…/.well-known/oauth-protected-resource/mcp", scope="mcp:read mcp:write"`
314
328
  (`error="invalid_token"` added when a token was sent); a revoked connection's
315
329
  token gets `{"error":"invalid_token","error_description":"This connection was revoked or lost its access. Connect again."}`.
316
330
  On `https://<app-host>/mcp` (where the manifest sets `mcp`), and on the Gateway with OAuth off:
@@ -8,7 +8,7 @@ by the agent that made the change, with no CI job and no API key.
8
8
  ## The app boundaries
9
9
 
10
10
  What the review looks for (what the Gateway promises and what the app must
11
- do itself is the threat model, `docs/threat-model.md` in the Every App
11
+ do itself is the threat model, `docs/threat-model.md` in the EveryApp
12
12
  checkout, served at https://everyapp.com/docs/agent/repo/threat-model):
13
13
 
14
14
  - A route, server function or tool that reads who the user is from anything
@@ -21,6 +21,8 @@ click when they have to do something themselves.
21
21
  | OpenRouter | [openrouter.md](openrouter.md) | `OPENROUTER_API_KEY` | OpenSEO, Open CRM, agentic-inbox |
22
22
  | OpenAI | [openai.md](openai.md) | `OPENAI_API_KEY`, `AI_PROXY_EXTRA_ROUTES` | apps listing the `openai` provider |
23
23
  | Workers AI | [workers-ai.md](workers-ai.md) | `WORKERS_AI_API_TOKEN` (`CLOUDFLARE_ACCOUNT_ID` is set by `everyapp setup`) | agentic-inbox (optional) |
24
+ | The default model provider (any one key) | [ai.md](ai.md) | one provider's key (OpenRouter first); the choice with `everyapp ai default` | every app whose manifest says `ai: { chat: true }` (agentic-inbox, OpenCRM) |
25
+ | Cloudflare AI Gateway in front of the models | [ai-gateway.md](ai-gateway.md) | `AI_GATEWAY_ID` (set by `everyapp ai gateway on`; optional `AI_GATEWAY_API_KEY`) | every AI provider call, once it is on |
24
26
  | Google (Gmail, Calendar, Search Console and Analytics) | [google.md](google.md) | `GOOGLE_WORKSPACE_CLIENT_ID`, `GOOGLE_WORKSPACE_CLIENT_SECRET` | Open CRM |
25
27
  | Microsoft 365 (Outlook mail and calendar) | [microsoft-365.md](microsoft-365.md) | `MICROSOFT_365_CLIENT_ID`, `MICROSOFT_365_CLIENT_SECRET` | Open CRM |
26
28
  | Slack (OAuth client) | [slack.md](slack.md) | `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET` | Open CRM |
@@ -49,8 +51,23 @@ whole spec, hint and smoke request included, and its setting is set like
49
51
  any Gateway setting (`pnpm everyapp secret set <NAME>`, which first names
50
52
  the provider and the hosts its key goes to).
51
53
 
54
+ An OAuth provider (GitHub, Notion, Linear, GitLab, Atlassian, HubSpot,
55
+ Zoom, Dropbox, Spotify, X, or any custom one; the agent docs'
56
+ `providers` page, "OAuth providers") has no file either: `pnpm everyapp
57
+ provider presets` lists what can be added, `pnpm everyapp provider add
58
+ --oauth <preset>` adds it and prints the redirect URI the person registers
59
+ at the provider's console (`https://<gateway>/connect/<id>/callback`) and
60
+ the two settings to set, `pnpm everyapp secret set <ID>_CLIENT_ID
61
+ <ID>_CLIENT_SECRET` (an owner or admin may paste them in Manage workspace →
62
+ Providers instead). Only a person can make the OAuth app at the provider
63
+ and approve a consent; after the client is set, each member connects their
64
+ own account under Connected accounts in the account menu. Google,
65
+ Microsoft 365 and Slack below are the same: their files set the client,
66
+ and the same client serves both the app-driven flow those files describe
67
+ and the Gateway-owned one.
68
+
52
69
  Which integrations a person needs: the apps they deploy decide. The four
53
- Every App apps (OpenSEO, OpenCRM, OpenAnalytics, Agentic Inbox) are
70
+ EveryApp apps (OpenSEO, OpenCRM, OpenAnalytics, Agentic Inbox) are
54
71
  installed from their GitHub releases by [gateway.md](gateway.md)'s last
55
72
  step, each unless the person skips it; the keys they then ask for are the
56
73
  files below. From any
@@ -15,10 +15,14 @@ team lacks it), an allow policy with every member's and invitee's email, two
15
15
  Access applications, and the three Gateway settings, in one run. At the end
16
16
  the person has Access's login before the Gateway, MCP clients, the Gateway's
17
17
  `/health` and each app's public routes still reachable without it, and
18
- optionally no passwords. A person running `pnpm everyapp setup` at a
19
- terminal is offered the same by setup itself ("Put Cloudflare Access in
20
- front of your Gateway?", Yes by default); under `--json` nobody is, so the
21
- offer is yours, after the owner exists. This
18
+ optionally no passwords. `pnpm everyapp setup` does the same by
19
+ default once the owner exists: a person at a terminal is asked ("Put
20
+ Cloudflare Access in front of your Gateway?", Yes by default); under
21
+ `--json` or `--yes` it goes on without a question when the account can (a
22
+ Zero Trust team exists, the Cloudflare login is an owner or admin of the
23
+ Gateway), and otherwise the warning `ACCESS_SETUP_SKIPPED` says why and
24
+ names `access on`: then the offer is yours. `setup --no-access` leaves it
25
+ off. This
22
26
  is the "outer ring" of `docs/runbooks/cloudflare-access.md`; the other way,
23
27
  a "Continue with Company SSO" button, is
24
28
  the runbook's "OIDC sign-in" and isn't covered here.
@@ -127,9 +131,9 @@ one-time PIN expires 10 minutes after it is sent.
127
131
  | `ACCESS_HOSTED` | a hosted Gateway ignores Access | not available there |
128
132
  | `ACCESS_SESSION_INVALID`, `ACCESS_EMAIL_INVALID`, `ACCESS_DOMAIN_INVALID` | a flag's value | durations like `720h` or `2h30m`, a full address, the part after the `@` |
129
133
  | warning `ACCESS_EMAIL_NOT_INVITED` | `--email` names someone without an account; the Gateway refuses them | invite them in **Manage workspace → Users** |
130
- | warning `ACCESS_IDP_UNVERIFIED_EMAILS` | a generic OIDC or SAML provider lets people pick their own email; `on` leaves it off the Every App applications | nothing, unless the person wants it allowed anyway: then they allow it in the dashboard, knowing the risk (runbook: "Verified emails") |
134
+ | warning `ACCESS_IDP_UNVERIFIED_EMAILS` | a generic OIDC or SAML provider lets people pick their own email; `on` leaves it off the EveryApp applications | nothing, unless the person wants it allowed anyway: then they allow it in the dashboard, knowing the risk (runbook: "Verified emails") |
131
135
  | warning `ACCESS_ONE_APPLICATION` | one Access application covers both hosts | kept and updated; an app host's Access cookie is then a Gateway login too |
132
- | warning `ACCESS_FOREIGN_DESTINATION` | the application also covers something that isn't Every App's | kept as found; `access off` won't delete that application |
136
+ | warning `ACCESS_FOREIGN_DESTINATION` | the application also covers something that isn't EveryApp's | kept as found; `access off` won't delete that application |
133
137
  | warning `ACCESS_INLINE_POLICIES` | the application has policies of its own, so the shared policy can't attach | the person makes them reusable in the dashboard, or manages it there |
134
138
  | warning `ACCESS_ROUTE_NOT_EXPRESSIBLE`, `ACCESS_TOO_MANY_PUBLIC_ROUTES` | a public route stays behind Access: no Access pattern for it (one `*` per path segment), or past the 25 overrides per host the command allows (the runbook's limit; Cloudflare's account limits page lists none) | people signed in still reach it; merge routes under one prefix |
135
139
  | warning `ACCESS_APP_OUTSIDE_BASE` | the app's host isn't one label under `<app base domain>`, so the apps' application on `*.<app base domain>` doesn't cover it: Access isn't in front of that app at all (the Gateway still signs people in there); merging routes changes nothing | the person moves the app under the base domain, or makes an Access application for that host by hand in **Zero Trust → Access controls → Applications** |
@@ -146,7 +150,7 @@ one-time PIN expires 10 minutes after it is sent.
146
150
  | MCP clients get an HTML login page | a bypass is missing | `pnpm everyapp access on --json` |
147
151
  | teammates can't log in at Access at all | the team's only provider is Sign in with Cloudflare, restricted to the account's members | `pnpm everyapp access on --json` adds the one-time PIN beside it, on the applications too |
148
152
  | Access's forbidden page after **Sign in with Cloudflare** | the person's Cloudflare account email isn't a Gateway member (`ACCESS_LOGIN_NOT_MEMBER`) | use **Send login code** with the Gateway address, or invite the Cloudflare one |
149
- | `ACCESS_IDP_ANY_CLOUDFLARE_ACCOUNT` from `on` or `show` | a Cloudflare provider's **Restrict to account members** is off; `on` leaves it off the Every App applications (the PIN alone signs people in) | the person turns it on: Zero Trust → Settings → Authentication → the provider; then `pnpm everyapp access on --json` allows it |
153
+ | `ACCESS_IDP_ANY_CLOUDFLARE_ACCOUNT` from `on` or `show` | a Cloudflare provider's **Restrict to account members** is off; `on` leaves it off the EveryApp applications (the PIN alone signs people in) | the person turns it on: Zero Trust → Settings → Authentication → the provider; then `pnpm everyapp access on --json` allows it |
150
154
  | the 51st person is blocked by Access | the Free plan's 50 seats | the person removes inactive users (Zero Trust → **Team & Resources → Users** → **Action → Remove users**; `cf zero-trust seats edit` does it from the shell, only when they ask), or pays |
151
155
  | `EMAIL_PASSWORD_SIGN_IN=false` changes nothing | no owner has linked Access yet (no log line for it: `pnpm everyapp access show --json` still lists the owner in `data.linkPending`), or Access isn't configured (`access.config_problem` in the logs) | an owner signs in through Access once; or fix the settings |
152
156
  | locked out: nobody passes Access | the policy or the one-time PIN is wrong at Cloudflare's edge; the Worker's settings can't let anyone in | when the person asks: `pnpm everyapp access off --json --yes` (deletes the two applications too); or the person fixes the policy, or deletes the applications, in **Zero Trust → Access controls → Applications** |
@@ -0,0 +1,117 @@
1
+ # Cloudflare AI Gateway in front of the model providers
2
+
3
+ Cloudflare AI Gateway is a proxy for model calls in the Gateway's own
4
+ Cloudflare account: a log of every call, caching, rate limits, retries
5
+ and a spend view, at `https://gateway.ai.cloudflare.com/v1/<account>/<gateway>/…`.
6
+ It has no models of its own and never replaces a provider's key: with it
7
+ on, every call the Gateway makes to an AI provider that has an AI Gateway
8
+ slug (OpenRouter, OpenAI, Anthropic, Groq, Mistral, Workers AI) goes
9
+ through it with the provider's own key still on the call; Gemini and
10
+ Cohere go direct (Cloudflare documents no form for their OpenAI-shaped
11
+ paths). It changes no app and no manifest. At the end the person has an
12
+ AI Gateway named `everyapp` in their account, `AI_GATEWAY_ID` on the
13
+ Gateway, and every model call showing up in the AI Gateway dashboard.
14
+
15
+ ## Settings
16
+
17
+ | NAME | Required or optional | What it holds |
18
+ | -------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
19
+ | `AI_GATEWAY_ID` | optional (set by `pnpm everyapp ai gateway on`) | the AI Gateway's id, its name in the dashboard (`everyapp`); with `CLOUDFLARE_ACCOUNT_ID`, which `everyapp setup` set |
20
+ | `AI_GATEWAY_API_KEY` | optional, only for an authenticated gateway | an AI Gateway token with Run permission, sent as `cf-aig-authorization: Bearer <token>` |
21
+
22
+ The Gateway sends the gateway token only to `gateway.ai.cloudflare.com`
23
+ and, for Workers AI, `api.cloudflare.com` with `cf-aig-gateway-id`. Apps
24
+ can't set `cf-aig-*` headers: the proxy strips them.
25
+
26
+ ## Costs and plans
27
+
28
+ As of October 2026: AI Gateway is included with a Cloudflare account on
29
+ every plan, with logs kept per plan (Workers Free: 100,000 logs per
30
+ gateway; Paid: 10 million) and no per-request charge of its own; the
31
+ provider's prices are unchanged (BYOK: the provider's key is the
32
+ provider's bill). Unified Billing (paying providers through Cloudflare
33
+ credits) is not used: the Gateway always sends the provider's key, and
34
+ `cf-aig-no-wholesale` is not needed because a key on the request wins.
35
+
36
+ ## Only a person can
37
+
38
+ - Decide to turn it on (it adds a hop and a log of every prompt's
39
+ metadata, never the prompt itself unless logging of payloads is turned
40
+ on in the dashboard).
41
+ - Create a gateway token in the dashboard when they want an
42
+ authenticated gateway, and paste it into the terminal.
43
+ - Turn on caching, rate limits or payload logging in the AI Gateway
44
+ dashboard: the Gateway sends no `cf-aig-*` options; the gateway's own
45
+ settings apply to every call.
46
+
47
+ ## Before you start
48
+
49
+ 1. `pnpm everyapp status --json`: `data.installed` and `data.claimed`
50
+ true; otherwise [gateway.md](gateway.md) first.
51
+ 2. `pnpm everyapp ai show --json`: `done.data.aiGateway.on` false means
52
+ it isn't on; `done.data.via` says `direct`. At least one model
53
+ provider configured ([ai.md](ai.md)) so there is something to front.
54
+ 3. The Cloudflare login the CLI uses can create AI Gateways in the
55
+ account (the account owner's login, or a token with AI Gateway Write).
56
+
57
+ ## Steps
58
+
59
+ 1. **Turn it on**: `pnpm everyapp ai gateway on --json`. The CLI finds or
60
+ creates an AI Gateway named `everyapp` in the Gateway's account
61
+ (`--id <name>` for another name, or one the person made already) and
62
+ sets `AI_GATEWAY_ID` on the Gateway. `done.data` is `{ id, url,
63
+ created }`. From the Gateway's next request every slugged provider
64
+ call goes through it.
65
+ 2. **Authenticated gateway (optional).** In the dashboard, AI → AI
66
+ Gateway → the gateway → Settings → Authenticated Gateway: on; create
67
+ an AI Gateway token (Run permission). Then from the person's
68
+ terminal: `pnpm everyapp secret set AI_GATEWAY_API_KEY` (the value by
69
+ pipe or prompt, never on the command line). Without the token on an
70
+ authenticated gateway every model call answers 401 from AI Gateway.
71
+ 3. Nothing else: no app changes, no key moves.
72
+
73
+ ## Verify
74
+
75
+ 1. `pnpm everyapp ai show --json`: `done.data.aiGateway` is `{ on: true,
76
+ id: "everyapp" }` and `done.data.via` is `ai-gateway`.
77
+ 2. Make one model call in an app (ask Agentic Inbox's agent something),
78
+ then open the dashboard: AI → AI Gateway → `everyapp` → Logs shows
79
+ the request with the provider's name.
80
+ 3. Manage workspace → Usage still counts the call under its provider:
81
+ AI Gateway adds a hop, not a provider.
82
+
83
+ ## If it fails
84
+
85
+ - `ai gateway on` fails with a Cloudflare error: the login can't create
86
+ AI Gateways (a scoped token without AI Gateway Write); use the
87
+ account owner's login, or make the gateway in the dashboard and run
88
+ `ai gateway on --id <its name>`.
89
+ - A model call answers 401 after turning it on: the gateway is
90
+ authenticated and `AI_GATEWAY_API_KEY` isn't set (step 2) or is wrong
91
+ (rotate it).
92
+ - A model call answers 404 or 400 from `gateway.ai.cloudflare.com`: a
93
+ provider path AI Gateway doesn't serve the way the catalog recorded
94
+ it (the entry's `aig` block; `landing-page/content/agent/ai.md` lists
95
+ each). `pnpm everyapp ai gateway off` sends everything direct again
96
+ while it is looked at.
97
+ - Calls work but don't appear in the dashboard: the provider has no
98
+ slug (Gemini, Cohere) and went direct.
99
+
100
+ ## Rerun, rotate, remove
101
+
102
+ - `ai gateway on` again is safe: it finds the existing gateway and
103
+ rewrites the same id.
104
+ - Rotate the gateway token in the dashboard, then `pnpm everyapp secret
105
+ set AI_GATEWAY_API_KEY` again.
106
+ - `pnpm everyapp ai gateway off` unsets `AI_GATEWAY_ID`: every call goes
107
+ direct from the next request; the AI Gateway resource and its logs
108
+ stay in the account (delete it in the dashboard). `pnpm everyapp secret
109
+ unset AI_GATEWAY_API_KEY` removes the token.
110
+
111
+ ## Sources
112
+
113
+ - https://developers.cloudflare.com/ai-gateway/ (overview; provider
114
+ pages under usage/providers/; authentication under
115
+ configuration/authentication/; the header glossary), read 2026-10-25.
116
+ - https://everyapp.com/docs/agent/ai (in the checkout
117
+ `landing-page/content/agent/ai.md`): how each provider is fronted.
@@ -0,0 +1,139 @@
1
+ # The default model provider
2
+
3
+ An app that says `ai: { chat: true }` in its manifest (Agentic Inbox,
4
+ OpenCRM's research agent) takes whichever chat model the Gateway's
5
+ operator made the default, through `/api/ai/default/…`; it never names a
6
+ provider. One key, set once, makes every such app work. This file sets
7
+ that one key and, when the person wants a particular provider or model,
8
+ the choice. At the end the person has one model provider's key on the
9
+ Gateway, `pnpm everyapp ai show` saying what the default resolves to,
10
+ and every app that needs "a chat model" open.
11
+
12
+ ## Settings
13
+
14
+ | NAME | Required or optional | What it holds |
15
+ | -------------------------------------------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
16
+ | one provider's key (`OPENROUTER_API_KEY` first in the order) | required by every app whose manifest says `ai.chat` | the provider's key as its file says ([openrouter.md](openrouter.md), [workers-ai.md](workers-ai.md), [openai.md](openai.md), or [providers.md](providers.md) under `anthropic`, `groq`, `mistral`, `gemini`) |
17
+ | the choice (`pnpm everyapp ai default <provider>[/<model>]`) | optional | which provider (and model) the default is; without it the order decides |
18
+
19
+ The order the Gateway resolves the default in, the first whose settings
20
+ are set: `openrouter` (one key, many models, the price in every answer),
21
+ `workers-ai` (no provider account: the Gateway's own Cloudflare token;
22
+ weaker models), `openai`, `anthropic`, `groq`, `mistral`, `gemini`. The
23
+ operator's choice wins over the order. Cloudflare AI Gateway is never
24
+ the default: it fronts whichever provider resolves
25
+ ([ai-gateway.md](ai-gateway.md)).
26
+
27
+ ## Costs and plans
28
+
29
+ The provider's: OpenRouter is prepaid in USD at the providers' own
30
+ prices ([openrouter.md](openrouter.md)); Workers AI bills the Cloudflare
31
+ account per token ([workers-ai.md](workers-ai.md)); the others each have
32
+ a plan of their own, named in their section of
33
+ [providers.md](providers.md). The model the Gateway fills in when an app
34
+ names none is a cheap capable one per provider (OpenRouter
35
+ `anthropic/claude-haiku-5.5`, Workers AI
36
+ `@cf/meta/llama-4-scout-17b-16e-instruct`, OpenAI `gpt-6-luna`, Groq
37
+ `openai/gpt-oss-20b`, Mistral `mistral-small-latest`, Gemini
38
+ `gemini-3.1-flash-lite`); `pnpm everyapp ai default <provider>/<model>`
39
+ picks another. Usage by provider and model is on Manage workspace →
40
+ Usage and in `pnpm everyapp workspace usage --json` (`byModel`).
41
+
42
+ ## Only a person can
43
+
44
+ - Open an account with the provider and buy credits or add a card.
45
+ - Create the key there and paste it into the terminal (the value never
46
+ goes through the chat or the command line).
47
+ - Decide the provider and model when the shipped order isn't what they
48
+ want (a model they trust, a region, a budget).
49
+
50
+ ## Before you start
51
+
52
+ 1. `pnpm everyapp status --json`: `data.installed` and `data.claimed`
53
+ true; otherwise [gateway.md](gateway.md) first.
54
+ 2. Whether this file applies: an app in `data.apps[]` whose `setup.unmet`
55
+ has a setting with `kind: "ai-default"` (label "A chat model (…)"),
56
+ or whose `setup.settings` lists one. Its `names` is the key the order
57
+ asks for first and its `hint` names the alternatives. No such app:
58
+ nothing to do here; an app that pins a provider is that provider's
59
+ file.
60
+ 3. `pnpm everyapp ai show --json`: `done.data.configured` false means no
61
+ provider is set yet; `done.data.provider` and `done.data.settings`
62
+ say which key the default would use; `done.data.candidates[]` lists
63
+ every provider with `configured`.
64
+
65
+ ## Steps
66
+
67
+ 1. **Pick the provider with the person.** Default to OpenRouter unless
68
+ they say otherwise: it is one key for every model and the only one
69
+ whose spend the Gateway counts in dollars. Workers AI when they want
70
+ no provider account at all. A single vendor when they already have a
71
+ key there.
72
+ 2. **Set that provider's key**, by its own file: [openrouter.md](openrouter.md)
73
+ (`OPENROUTER_API_KEY`), [workers-ai.md](workers-ai.md)
74
+ (`WORKERS_AI_API_TOKEN`), [openai.md](openai.md) (`OPENAI_API_KEY`),
75
+ or the provider's section of [providers.md](providers.md)
76
+ (`ANTHROPIC_API_KEY`, `GROQ_API_KEY`, `MISTRAL_API_KEY`,
77
+ `GEMINI_API_KEY`). Each ends with `pnpm everyapp secret set <NAME>`,
78
+ the value from the person's terminal, or `pnpm everyapp configure
79
+ <app id>`, which asks for exactly the one key the app's `ai-default`
80
+ setting names.
81
+ 3. **Store the choice when it isn't the order's.** The first configured
82
+ provider of the order is the default by itself; nothing to store when
83
+ it is OpenRouter and the person is happy with its model. Otherwise:
84
+ `pnpm everyapp ai default <provider>` (`groq`), or with a model,
85
+ `pnpm everyapp ai default openrouter/anthropic/claude-sonnet-5.5`
86
+ (the provider id up to the first slash, the model after it). `pnpm
87
+ everyapp ai default off` clears it. Each change queues the
88
+ `ai.default_changed` event.
89
+ 4. **Optionally put AI Gateway in front**: [ai-gateway.md](ai-gateway.md)
90
+ (`pnpm everyapp ai gateway on`), for logs, caching and limits on
91
+ every model call; it changes no app and no key.
92
+
93
+ ## Verify
94
+
95
+ 1. `pnpm everyapp ai show --json`: `done.data.configured` true,
96
+ `done.data.provider` the one the person picked (or the order's
97
+ first), `done.data.model` the model that will be filled in,
98
+ `done.data.via` `ai-gateway` once the gateway is on, else `direct`.
99
+ 2. `pnpm everyapp status --json`: every app that said `ai.chat` has
100
+ `setup.ready` true and no `ai-default` setting in `setup.unmet`.
101
+ 3. In the app: a chat answer arrives (Agentic Inbox: open a thread and
102
+ ask the agent; OpenCRM: run research on a company). Manage workspace
103
+ → Usage → By provider shows the provider and, under it, the model.
104
+
105
+ ## If it fails
106
+
107
+ - `ai_default_not_configured` in an app's error (503): no provider's
108
+ key is set, or the chosen one's isn't. The message names the key;
109
+ step 2. `pnpm everyapp ai show` says the same with `configured: false`.
110
+ - `AI_DEFAULT_INVALID` from `ai default`: the id isn't a provider the
111
+ Gateway knows, is an API provider (Stripe), has no OpenAI-shaped chat
112
+ route (Cohere: an app pins it under `providers`), or is `ai-gateway`
113
+ (turn it on with `ai gateway on` and pick a model provider). The
114
+ message names the candidates.
115
+ - `ai_route_not_allowed` on the default route: the app called something
116
+ other than chat completions or embeddings; it lists the provider under
117
+ `providers` for that.
118
+ - `model_not_allowed` never comes from the default route; an app seeing
119
+ it pinned a provider and limited `ai.models`.
120
+ - A provider's own error (401, 429, a model name it doesn't serve): the
121
+ provider's file, "If it fails".
122
+
123
+ ## Rerun, rotate, remove
124
+
125
+ - Rerunning any step is safe: `ai default` overwrites the choice, a key
126
+ set again replaces the old one on the next request.
127
+ - Rotate a key as its provider's file says; the default resolves to the
128
+ same provider.
129
+ - Remove the choice with `pnpm everyapp ai default off`; remove a key
130
+ with `pnpm everyapp secret unset <NAME>`, after which the order moves
131
+ to the next configured provider, or apps that need the default show
132
+ "Needs setup" again.
133
+
134
+ ## Sources
135
+
136
+ - https://everyapp.com/docs/agent/ai (in the checkout
137
+ `landing-page/content/agent/ai.md`): the route, the order, the
138
+ commands and their `--json`, the event.
139
+ - `packages/sdk/src/internal/aiDefault.ts`: the resolution rule.
@@ -54,12 +54,12 @@ As of October 2026 (context.dev/pricing and docs.context.dev/account):
54
54
  2. **Person:** Sign up, or sign in. Page: https://www.context.dev/signup. Type their email in **Email** and click **Continue** (an email sign-up sets a password and asks them to verify the address by email), or pick Google or GitHub under "or continue with" (no verification step). Continuing accepts the Terms of Service and Privacy Policy. An existing account signs in from "Already have an account? Sign in" (https://context.dev/login). It worked when the dashboard opens.
55
55
 
56
56
  Alternative, only with their say-so (it registers a key in their name): Context.dev's agent flow (context.dev/auth.md, read today, not run). Everything in one shell, with `EMAIL` set to the address they gave (an existing account's email, when they have one). Register:
57
- `reg=$(jq -n --arg email "$EMAIL" '{type:"service_auth",login_hint:$email,api_key_name:"Every App Gateway",client_name:"everyapp setup"}' | curl -sS -X POST https://www.context.dev/agent/identity -H 'Content-Type: application/json' -d @-); printf '%s' "$reg" | jq '{claim, offer}'`
57
+ `reg=$(jq -n --arg email "$EMAIL" '{type:"service_auth",login_hint:$email,api_key_name:"EveryApp Gateway",client_name:"everyapp setup"}' | curl -sS -X POST https://www.context.dev/agent/identity -H 'Content-Type: application/json' -d @-); printf '%s' "$reg" | jq '{claim, offer}'`
58
58
  Show them `claim.verification_uri` and `claim.user_code` in full, and that both expire in `claim.expires_in` seconds (600: ten minutes). If the response has `offer`, say it includes 1,000 extra credits (deadline `offer.claim_by`); promise nothing otherwise. A 429 `rate_limit_exceeded` from the registration means stop, no retry: step 3 instead. They open the link, enter the 6-digit code, and sign up or sign in with that email (a password sign-up also verifies the address). Then poll every `claim.interval` seconds, for ten minutes at most, until the key arrives or an error ends it, and pipe the key straight in (README's "a command produced it"); `claim_token` is read from `$reg` in the same shell, never typed (it is short-lived and not the key):
59
59
  `CLAIM=$(printf '%s' "$reg" | jq -r .claim_token); INTERVAL=$(printf '%s' "$reg" | jq -r '.claim.interval // 5'); for i in $(seq 1 120); do r=$(curl -sS -X POST https://www.context.dev/oauth2/token -d "grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=$CLAIM"); e=$(printf '%s' "$r" | jq -r '.error // empty'); case "$e" in '') break;; authorization_pending) ;; slow_down) sleep 10;; *) printf '%s' "$r" | jq '{error, error_description}'; break;; esac; sleep "$INTERVAL"; done; printf '%s' "$r" | jq -e .access_token >/dev/null && printf '%s' "$r" | jq -r .access_token | pnpm everyapp secret set CONTEXT_API_KEY --json`
60
60
  `authorization_pending` means they haven't finished (`email_verification_required`: the address isn't verified yet); `expired_token` means the ten minutes passed: register again, only with their agreement; `invalid_grant` means stop and show its `error_description`. When the loop ends without a key, nothing is set and the printed error says why (none printed: curl itself failed). Never print `$r`: the key is in it. This replaces steps 3 and 4: go to Verify (nobody but the Gateway holds the key now, so its smoke check is skipped; 1 and 3 there are the check).
61
61
 
62
- 3. **Person:** Create the key. Page: https://context.dev/dashboard/api-keys. Create a new key with the page's button (its label isn't in the docs); if the dialog shows the key once, copy it before closing. Keep **Full access** (the default; it covers the Data APIs the apps use and the usage read the smoke check uses), or choose **Restricted** with Data APIs = Use and "Account logs, usage, and browser profiles" = Read. Name it "Every App Gateway" when the form asks (renaming later needs a paid plan). It worked when the key is listed on that page. A Free organization holds at most two keys: with two there already, delete one they no longer use (whether a disabled key still counts toward the two isn't in the docs), or use the agent flow above, which needs no button.
62
+ 3. **Person:** Create the key. Page: https://context.dev/dashboard/api-keys. Create a new key with the page's button (its label isn't in the docs); if the dialog shows the key once, copy it before closing. Keep **Full access** (the default; it covers the Data APIs the apps use and the usage read the smoke check uses), or choose **Restricted** with Data APIs = Use and "Account logs, usage, and browser profiles" = Read. Name it "EveryApp Gateway" when the form asks (renaming later needs a paid plan). It worked when the key is listed on that page. A Free organization holds at most two keys: with two there already, delete one they no longer use (whether a disabled key still counts toward the two isn't in the docs), or use the agent flow above, which needs no button.
63
63
 
64
64
  4. **Person, then You:** Put it on the Gateway. README's first way (the key is on a screen they are looking at): they open a second terminal in the `every-app` folder and run `pnpm everyapp configure crm` (or `pnpm everyapp configure openseo`; no `--json`: with it nothing prompts), which asks for this key among the app's other unset settings (for OpenSEO it is optional: Yes to `Set the N optional settings too?` (`Set the optional setting too?` for one), then `CONTEXT_API_KEY (Enter to skip)`; once an app is ready, its menu `Everything <app> needs is set. Change one?` lists it), or `pnpm everyapp secret set CONTEXT_API_KEY` for just this one. The prompt is hidden (`CONTEXT_API_KEY`, under lines saying what it is for and where it comes from; `secret set` asks `Value for CONTEXT_API_KEY`); they paste, press Enter, and see `Set CONTEXT_API_KEY on the Gateway` (configure then says `crm is ready.` or what the app still needs, and `Live now. No deploy needed.`; `secret set` ends `Live now on <gateway host>. No deploy needed.`). Nothing is exported afterwards, so offer the smoke check (Verify 2, 0 credits) in that same terminal: the hidden read (README's third way: `read -rs CONTEXT_API_KEY && export CONTEXT_API_KEY`, paste, Enter), Verify 2's curl, they tell you its last line (200) and `credits_remaining`, then `unset CONTEXT_API_KEY`; declined, the app (Verify 3) is the check. README's third way from the start, when they read it in hidden instead (never a typed `export …=`, which the shell history keeps; run the smoke check below first): `pnpm everyapp configure <app id> --from-env --json`, whose `done` event has `data.set` `["CONTEXT_API_KEY"]` and `data.changed.gateway.names`, or `pnpm everyapp secret set CONTEXT_API_KEY --from-env CONTEXT_API_KEY --json`, whose `done` event has `data.gateway.names` `["CONTEXT_API_KEY"]`; then `unset CONTEXT_API_KEY`. Never on a command line, never in the chat (a key seen in the chat gets rotated: step 3 again). Wait for "done".
65
65
 
@@ -112,7 +112,7 @@ value is set.
112
112
  Confirm the email if asked: until it is confirmed the API answers 40104.
113
113
  When it worked, the dashboard at app.dataforseo.com opens and shows a $1
114
114
  balance. Already registered: sign in at https://app.dataforseo.com. Ran
115
- elsewhere: an OpenSEO deployed outside Every App held the base64 form of
115
+ elsewhere: an OpenSEO deployed outside EveryApp held the base64 form of
116
116
  the same credential, which works unchanged here (step 4's note), and if
117
117
  that deployment whitelisted its server's IP on API Access, the list has
118
118
  to be emptied (step 2). Wait for "I'm in".
@@ -197,7 +197,7 @@ value is set.
197
197
  (`https://<gateway host>/?setup=openseo`) means a required setting is
198
198
  still unset: Verify 1 says which. OpenSEO shows one message for a
199
199
  missing key and a rejected one alike: "DataForSEO isn't set up on your
200
- Every App gateway yet, or rejected the key it holds. An owner or admin
200
+ EveryApp gateway yet, or rejected the key it holds. An owner or admin
201
201
  sets it with pnpm everyapp secret set DATAFORSEO_API_KEY." Tell them apart with
202
202
  `pnpm everyapp secret list --json`: `DATAFORSEO_API_KEY` absent from
203
203
  `data.gateway.names` means step 4 did not land (behind the Gateway's
@@ -227,7 +227,7 @@ value is set.
227
227
  | `SECRET_SEVERAL_NAMES` | `--from-env` or a pipe with more than one name | One name per call |
228
228
  | `SECRET_EMPTY` | The `--from-env` variable is empty or not exported in that shell | The hidden read (README's third way) in that same terminal first, then rerun |
229
229
  | A browser opening OpenSEO is sent to `https://<gateway host>/?setup=openseo`; any other request gets 503 with the body `{"error":"app_needs_setup","app":"openseo"}` | The Gateway's setup gate: a required setting of OpenSEO is still unset (this key or the OpenRouter one; Context is optional) | `data.app.setup.command`, `pnpm everyapp configure openseo` (step 4), then Verify 1 |
230
- | `integration_not_configured` (503, header `x-everyapp-error`) | The Gateway holds no `DATAFORSEO_API_KEY`; OpenSEO shows it as "DataForSEO isn't set up on your Every App gateway yet, or rejected the key it holds. An owner or admin sets it with pnpm everyapp secret set DATAFORSEO_API_KEY." (behind the setup gate the app doesn't open in this state) | Steps 4 and 5; check the name's spelling in `data.gateway.names` |
230
+ | `integration_not_configured` (503, header `x-everyapp-error`) | The Gateway holds no `DATAFORSEO_API_KEY`; OpenSEO shows it as "DataForSEO isn't set up on your EveryApp gateway yet, or rejected the key it holds. An owner or admin sets it with pnpm everyapp secret set DATAFORSEO_API_KEY." (behind the setup gate the app doesn't open in this state) | Steps 4 and 5; check the name's spelling in `data.gateway.names` |
231
231
  | `capability_not_requested` (403) | The deployed manifest does not list `capabilities.dataforseo` | OpenSEO's does; a fork must add it to `everyapp.config.ts` and deploy again |
232
232
  | `integration_path_not_allowed` (403) | The app called an account-wide path (`appendix/user_data`, `appendix/webhook_resend`, `tasks_ready`, `id_list`, `errors`) | By design: one key serves every app. Check balance and limits from the terminal (Verify 2) |
233
233
  | `integration_method_not_allowed` (405) | A method other than GET or POST | DataForSEO reads with GET and POST only |
@@ -278,7 +278,7 @@ recipient address (20 on a hosted Gateway) and 5,000 a day per Gateway.
278
278
  is `false`).
279
279
  3. The provider alone, Cloudflare, with the person's yes (one counted
280
280
  email, unless `to` is a verified destination address):
281
- `cf email-sending send --body '{"from":"noreply@<your domain>","to":"<address>","subject":"Every App test","text":"It works."}'`
281
+ `cf email-sending send --body '{"from":"noreply@<your domain>","to":"<address>","subject":"EveryApp test","text":"It works."}'`
282
282
  sends without the Gateway. It arriving means the provider is done and a
283
283
  failed invite is the Gateway's side (its settings; the logs below). A
284
284
  refusal is the REST API's numeric code (`10203` sending disabled,