@mediamonks/canopy-cli 0.7.11 → 0.7.12

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 (108) hide show
  1. package/dist/{deploy-BCtEY1is.mjs → deploy-B1WrPq5W.mjs} +8 -1
  2. package/dist/{dev-DtoZrexp.mjs → dev-BnUYGQQm.mjs} +5 -4
  3. package/dist/dev-CySBxW7I.mjs +2 -0
  4. package/dist/{dev-worker-BZgtEM9V.mjs → dev-worker-IWnHvQ1f.mjs} +42 -7
  5. package/dist/{docs-BV9qhLJx.mjs → docs-DxB7NhZs.mjs} +1 -1
  6. package/dist/{feedback-Cb8ccQ_R.mjs → feedback-B-GXUBPs.mjs} +1 -1
  7. package/dist/index.mjs +10 -10
  8. package/dist/{local-C5AhA4GM.mjs → local-BGRd_FMZ.mjs} +6 -469
  9. package/dist/{package-BBpw-n7k.mjs → package-CDLufecy.mjs} +1 -1
  10. package/dist/{skill-DJHlTEhS.mjs → skill-BOWfqwr2.mjs} +1 -1
  11. package/dist/src-Bu4Ze32I.mjs +470 -0
  12. package/dist/{upgrade-B86Ao6T5.mjs → upgrade-fhfrid_O.mjs} +1 -1
  13. package/package.json +3 -3
  14. package/dist/dev-BYxSHUH6.mjs +0 -2
  15. package/dist/docs/ci.md +0 -50
  16. package/dist/docs/cli-create.md +0 -46
  17. package/dist/docs/cli-db.md +0 -86
  18. package/dist/docs/cli-delete.md +0 -44
  19. package/dist/docs/cli-deploy.md +0 -58
  20. package/dist/docs/cli-dev.md +0 -82
  21. package/dist/docs/cli-docs.md +0 -47
  22. package/dist/docs/cli-download.md +0 -42
  23. package/dist/docs/cli-egress.md +0 -37
  24. package/dist/docs/cli-feedback.md +0 -75
  25. package/dist/docs/cli-index.md +0 -61
  26. package/dist/docs/cli-init.md +0 -46
  27. package/dist/docs/cli-login.md +0 -43
  28. package/dist/docs/cli-logout.md +0 -32
  29. package/dist/docs/cli-logs.md +0 -57
  30. package/dist/docs/cli-open.md +0 -41
  31. package/dist/docs/cli-projects.md +0 -43
  32. package/dist/docs/cli-promote.md +0 -44
  33. package/dist/docs/cli-rollback.md +0 -45
  34. package/dist/docs/cli-skill.md +0 -39
  35. package/dist/docs/cli-staging.md +0 -54
  36. package/dist/docs/cli-token.md +0 -47
  37. package/dist/docs/cli-upgrade.md +0 -43
  38. package/dist/docs/cli-usage.md +0 -43
  39. package/dist/docs/cli-whoami.md +0 -46
  40. package/dist/docs/cli.md +0 -86
  41. package/dist/docs/data.md +0 -85
  42. package/dist/docs/deploy.md +0 -81
  43. package/dist/docs/dev.md +0 -72
  44. package/dist/docs/developers-agents-and-ai.md +0 -95
  45. package/dist/docs/developers-architecture.md +0 -106
  46. package/dist/docs/getting-started.md +0 -108
  47. package/dist/docs/index.json +0 -331
  48. package/dist/docs/infrastructure.md +0 -406
  49. package/dist/docs/migrations.md +0 -183
  50. package/dist/docs/overview-faq.md +0 -105
  51. package/dist/docs/overview-how-it-works.md +0 -82
  52. package/dist/docs/overview-use-cases.md +0 -76
  53. package/dist/docs/overview-what-is-canopy.md +0 -75
  54. package/dist/docs/platform-ai-usage.md +0 -65
  55. package/dist/docs/platform-client-access.md +0 -54
  56. package/dist/docs/platform-deploys-and-rollbacks.md +0 -93
  57. package/dist/docs/platform-egress.md +0 -75
  58. package/dist/docs/platform-feedback.md +0 -114
  59. package/dist/docs/platform-members-and-access.md +0 -71
  60. package/dist/docs/platform-project-data.md +0 -76
  61. package/dist/docs/platform-schedules.md +0 -90
  62. package/dist/docs/platform-sites-and-profiles.md +0 -61
  63. package/dist/docs/porting-nextjs.md +0 -233
  64. package/dist/docs/sdk-ai.md +0 -103
  65. package/dist/docs/sdk-auth.md +0 -57
  66. package/dist/docs/sdk-database.md +0 -126
  67. package/dist/docs/sdk-files.md +0 -82
  68. package/dist/docs/sdk-services.md +0 -58
  69. package/dist/docs/sdk.md +0 -126
  70. package/dist/docs/staging.md +0 -106
  71. package/dist/docs/static-serving.md +0 -318
  72. package/dist/docs/troubleshooting.md +0 -102
  73. package/dist/skill/canopy/SKILL.md +0 -160
  74. package/dist/templates/spa/.github/workflows/canopy-deploy.yml +0 -34
  75. package/dist/templates/spa/AGENTS.md +0 -47
  76. package/dist/templates/spa/README.md +0 -14
  77. package/dist/templates/spa/_gitignore +0 -4
  78. package/dist/templates/spa/canopy.json +0 -5
  79. package/dist/templates/spa/index.html +0 -12
  80. package/dist/templates/spa/package.json +0 -21
  81. package/dist/templates/spa/src/app.tsx +0 -25
  82. package/dist/templates/spa/src/canopy.ts +0 -5
  83. package/dist/templates/spa/src/main.tsx +0 -10
  84. package/dist/templates/spa/src/style.css +0 -29
  85. package/dist/templates/spa/tsconfig.json +0 -16
  86. package/dist/templates/spa/vite.config.ts +0 -7
  87. package/dist/templates/static/AGENTS.md +0 -45
  88. package/dist/templates/static/README.md +0 -9
  89. package/dist/templates/static/_gitignore +0 -2
  90. package/dist/templates/static/canopy.json +0 -4
  91. package/dist/templates/static/index.html +0 -21
  92. package/dist/templates/static/main.js +0 -9
  93. package/dist/templates/static/style.css +0 -29
  94. package/dist/templates/worker/.dev.vars.example +0 -9
  95. package/dist/templates/worker/.github/workflows/canopy-deploy.yml +0 -34
  96. package/dist/templates/worker/AGENTS.md +0 -63
  97. package/dist/templates/worker/README.md +0 -17
  98. package/dist/templates/worker/_gitignore +0 -5
  99. package/dist/templates/worker/canopy.json +0 -6
  100. package/dist/templates/worker/index.html +0 -12
  101. package/dist/templates/worker/package.json +0 -22
  102. package/dist/templates/worker/src/app.tsx +0 -25
  103. package/dist/templates/worker/src/canopy.ts +0 -5
  104. package/dist/templates/worker/src/main.tsx +0 -10
  105. package/dist/templates/worker/src/style.css +0 -29
  106. package/dist/templates/worker/tsconfig.json +0 -16
  107. package/dist/templates/worker/vite.config.ts +0 -7
  108. package/dist/templates/worker/worker/index.ts +0 -52
@@ -1,114 +0,0 @@
1
- ---
2
- title: Feedback & bug reports
3
- description: A built-in feedback widget on every project, a ticket-lite inbox behind it, and a CLI that lets agents work the backlog.
4
- ---
5
-
6
- Every Canopy project gets an in-app feedback channel without writing a line of
7
- code: signed-in Monks see a small floating button on the live app, pick **Bug**
8
- or **Feedback**, type what happened, and send. The report lands in the
9
- project's **Feedback tab** on the dashboard, with everything an investigator
10
- needs captured automatically — page URL, the deploy version that served it,
11
- viewport, and recent console errors.
12
-
13
- It is deliberately **not** a ticketing product: an item has a status, an
14
- append-only comment trail, and nothing else. No assignees, priorities, or
15
- boards.
16
-
17
- ## The widget
18
-
19
- - Appears only for **signed-in Monks**. Client-password visitors never see it —
20
- the platform doesn't even send them the script.
21
- - On by default for every project. Turn it off per project in
22
- **Settings → Feedback widget**; the change applies on the next page load.
23
- - Submissions are always identified (your company sign-in), so there's no
24
- anonymous spam and no reporter field to fill in.
25
- - The widget also shows your own previous reports for that project with their
26
- current status.
27
- - A page can reshape the chooser with meta tags — this docs site does exactly
28
- that: `<meta name="canopy-feedback-kinds" content="docs,feedback">` swaps
29
- "Bug" for "Docs issue", and `canopy-feedback-prompt` replaces the textarea
30
- placeholder. Kinds are limited to `bug`, `feedback`, `docs`.
31
-
32
- ## Screenshots
33
-
34
- A screenshot is often the whole report, so you can attach up to six to any
35
- report:
36
-
37
- - **In the widget:** click **Attach screenshot**, or paste one straight from
38
- the clipboard into the message box, or drop an image on the panel. Thumbnails
39
- show what will be sent, and each one can be removed before you hit Send.
40
- - **On the dashboard:** the same three ways, from the report itself — so you can
41
- add a picture to a report after filing it, or to a comment as you write it
42
- ("here's what it looks like after the fix"). Click any thumbnail for the full
43
- image, then use the arrow keys to page through the rest.
44
- - **From the CLI:** `canopy feedback report "…" --attach ./shot.png`, or
45
- `canopy feedback attach <id> ./after-fix.png` for a report that already
46
- exists. `canopy feedback show <id> --download <dir>` saves a report's
47
- screenshots so an agent can look at them.
48
-
49
- PNG, JPEG, WebP and GIF, up to 5 MB each. Large screenshots are scaled down in
50
- your browser before they're sent, so a full-screen retina capture goes up small
51
- without you doing anything. Screenshots are visible to the same people as the
52
- report itself, and are deleted with it.
53
-
54
- ## The inbox
55
-
56
- Project members triage reports on the **Feedback tab**:
57
-
58
- - Statuses: `investigate → investigated → accepted → in_progress → resolved`,
59
- plus `declined`.
60
- - Everything arrives as **investigate** — nobody has looked at it yet. An agent
61
- can pick those up, work out what is actually going on, and write the findings
62
- on the report as **investigated**.
63
- - **`accepted` means a person said "build this."** It is the one status an
64
- agent should never reach for on its own.
65
- - The order is a suggestion, not a track. Accept something straight away, skip
66
- the investigation, or decline it at any point.
67
- - Every status change and comment is recorded on the item's trail.
68
- - Reporters follow their submissions on the **Reports page** (dashboard
69
- header) and can comment on or withdraw their own items; only project
70
- members change statuses.
71
-
72
- ## Working the backlog with an agent
73
-
74
- Because the CLI exposes the inbox, "look into the new reports" and "work
75
- through the accepted feedback" are both prompts, not processes:
76
-
77
- ```bash
78
- canopy feedback list --status investigate # what nobody has looked at yet
79
- canopy feedback show <id> # body + captured context + trail
80
- canopy feedback status <id> investigated \
81
- --comment "root cause: the upload grant expires too early"
82
- canopy feedback list --status accepted # what a person asked for
83
- canopy feedback status <id> resolved --comment "fixed in v42"
84
- ```
85
-
86
- `investigated` always carries the findings — the note is required, because a
87
- report that claims someone looked into it and shows nothing is worse than an
88
- untouched one. Then it waits: **accepting is the person's call**, so an agent
89
- sets `accepted` only when the person it is working with says to build the
90
- thing.
91
-
92
- `show` includes the deploy version that served the page when the bug was
93
- reported — reproduce against that, not whatever is live now. A report worth
94
- pushing back on gets `status <id> declined` with a comment saying why.
95
-
96
- ## Reporting problems with Canopy itself
97
-
98
- When a `canopy` command fails while you (or your agent) are building a
99
- project, file it against the **platform inbox** — not your own project:
100
-
101
- ```bash
102
- canopy feedback report "deploy failed after upload" \
103
- --command "canopy deploy --staging" --error "upload_grant_expired …"
104
- ```
105
-
106
- With no `--project`, the report goes to the platform team automatically, with
107
- your CLI version, site, and the project you were working on attached. Before
108
- filing: rule out user error first (`canopy docs troubleshooting`,
109
- `vp env doctor` for toolchain issues), and skim `canopy feedback mine` to
110
- avoid filing the same issue twice.
111
-
112
- ## CLI reference
113
-
114
- See [canopy feedback](https://docs.canopy.website/cli/feedback) for every subcommand and flag.
@@ -1,71 +0,0 @@
1
- ---
2
- title: Members & access
3
- description: Everything on Canopy sits behind Monks sign-in. Collaborators manage a project; other Monks can view the live app — unless you tighten it.
4
- ---
5
-
6
- Nothing on Canopy is reachable by the anonymous internet. Every project URL
7
- and the dashboard itself sit behind Monks OneLogin — visitors sign in with
8
- their company account before they see anything. On top of that wall, each
9
- project has its own roster that decides who can _manage_ it and, optionally,
10
- who can _view_ it.
11
-
12
- ## The default: any Monk can look, members can manage
13
-
14
- Out of the box:
15
-
16
- - **Any signed-in Monk** can open a project's live URL and see the app. The
17
- project also appears in the company-wide list on the dashboard.
18
- - **Only the project's members** (and platform admins) can open its
19
- management pages — deploy history, data browser, usage, and settings.
20
-
21
- That default fits most internal work: colleagues can try what you built, but
22
- nobody outside the team can touch its data or settings.
23
-
24
- ## Roles
25
-
26
- - **Owner** — the person who created the project. Owners govern it: they
27
- invite and remove collaborators, change access settings, and can delete
28
- the project.
29
- - **Collaborator** — invited teammates. They can do day-to-day work — deploy,
30
- roll back, browse and edit data, manage schedules and secrets — but not
31
- delete the project or manage its roster.
32
- - **Platform admin** — can manage any project from the dashboard. Admins keep
33
- governance access, but they do not silently bypass a collaborators-only
34
- live app (see below).
35
-
36
- ## Inviting collaborators
37
-
38
- Open the project's **Settings** tab and use the **Collaborators** card. A
39
- searchable people picker finds anyone who has signed in to Canopy; adding
40
- them takes effect immediately. Owners (and admins) can also remove people
41
- from the same card.
42
-
43
- ## Collaborators-only mode
44
-
45
- Sometimes "any Monk can look" is too open — payroll prototypes, sensitive
46
- client work, unfinished things you'd rather not demo by accident. Flip on
47
- **Settings → Who at Monks can open it → Restrict to collaborators** and the
48
- live app is limited to the project's roster:
49
-
50
- - Other Monks who open the URL get a clear "limited to its collaborators"
51
- page instead of the app.
52
- - The project still appears in the company list — it isn't hidden, just
53
- closed.
54
- - Platform admins can still manage the project in the dashboard, but they
55
- must add themselves as collaborators if they need to open the live app.
56
- - [Client password access](https://docs.canopy.website/platform/client-access), if you've enabled
57
- it, is unaffected — the two controls are independent.
58
-
59
- Turning it off again is the same toggle.
60
-
61
- > **Note:** Collaborators-only gates the **live app**. Management access (settings, data, deploys) is always
62
- > members-only, regardless of this toggle.
63
-
64
- ## Platform admins and the Users page
65
-
66
- Platform admins get an extra **Users** page in the dashboard listing everyone
67
- on Canopy — name, email, and when they first signed in — with a one-click way
68
- to grant or remove admin. Accounts are created automatically on first
69
- OneLogin sign-in; there is nothing to provision.
70
-
71
- - [Client access](https://docs.canopy.website/platform/client-access) — Share work-in-progress with clients who don't have a Monks account.
@@ -1,76 +0,0 @@
1
- ---
2
- title: Project data
3
- description: Every project gets its own private database and file storage — browse it in the dashboard, snapshot it before risky changes, and pull it locally.
4
- ---
5
-
6
- Every Canopy project comes with its own private database and file storage,
7
- created automatically the moment the project exists. There are no connection
8
- strings to configure and no servers to run — your app reads and writes through
9
- the Canopy SDK, and no other project can ever see your data.
10
-
11
- ## The Data tab and the data explorer
12
-
13
- Open a project in the dashboard and the **Data** tab shows what your app has
14
- stored — every collection and table at a glance — plus the door into the
15
- **data explorer**, a full-page workspace for actually digging in:
16
-
17
- - **Schema sidebar** — collections and tables, each expandable to its
18
- columns (types, primary keys) and indexes.
19
- - **Browse** — click any table or collection to page through it with real
20
- totals, sort by any column, and stack filters (equals, contains,
21
- comparisons, null checks) without writing SQL. Click a row for the full
22
- record, JSON pretty-printed. Export the current page as CSV or JSON.
23
- - **SQL console** — a real editor with syntax highlighting and autocomplete
24
- fed by your schema. Run with <kbd>⌘⏎</kbd>, see row counts and timing,
25
- recall earlier queries from history, and hit **Explain** to see the query
26
- plan.
27
-
28
- The console is **read-only by default** — the platform rejects anything but
29
- reads until you flip the writes toggle, so exploring production data is safe
30
- by design. The explorer is fixed to the channel (live or staging) you opened
31
- it from; to look at the other one, go back to the Data tab and switch there.
32
-
33
- The **Files** tab next to it lists the project's blob storage — the uploads
34
- and files your app has saved.
35
-
36
- ## Snapshots and restores
37
-
38
- The Data tab's **Data safety** card is your undo button for data:
39
-
40
- - **Snapshot** stores a full dump of the database on the platform. Take one
41
- before a risky change — a big import, a cleanup script, letting an agent
42
- loose on production data.
43
- - **Restore** replaces the project's current data with a snapshot. This is
44
- destructive by design (that's the point), so the dashboard asks you to
45
- confirm.
46
- - **Export** downloads a dump to your machine.
47
-
48
- Snapshots live server-side and stay available until you delete the project,
49
- so you can restore from days ago, not just from your last session.
50
-
51
- > **Warning:** Restoring replaces **all** current data with the snapshot's contents. Take a fresh snapshot first
52
- > if there's anything recent you might want back.
53
-
54
- ## The same tools from the terminal
55
-
56
- Everything above is also available to you (and your coding agent) via the CLI:
57
-
58
- ```bash
59
- canopy db pull # download a fresh SQL dump
60
- canopy db snapshot # store a dump server-side
61
- canopy db snapshots # list stored snapshots
62
- canopy db restore # replace project data with a snapshot
63
- ```
64
-
65
- `canopy db pull` is also how you develop against realistic data: pull a dump,
66
- then run `canopy dev --local --pull` for a throwaway sandbox seeded with it.
67
-
68
- ## Portable by default, SQL when you need it
69
-
70
- The SDK's document API (`canopy.db.collection(...)`) behaves the same
71
- everywhere Canopy runs. Raw SQL via `canopy.db.sql()` passes straight through
72
- to the underlying database — SQLite on the Cloudflare sites, Postgres on the
73
- GCP pilot — so anything that database supports works. Your code can ask
74
- `canopy.db.dialect()` which one it's talking to.
75
-
76
- - [Working with project data](https://docs.canopy.website/guides/data) — Collection naming rules, dialects and raw SQL, dumps, and restore workflows in depth.
@@ -1,90 +0,0 @@
1
- ---
2
- title: Schedules
3
- description: Run named cron jobs in your project's backend — configured in the dashboard, so you can change the cadence without redeploying.
4
- ---
5
-
6
- Schedules let a project do things on a clock: send a daily digest, refresh a
7
- cache every hour, tick a counter every minute. You define **named jobs** in
8
- the dashboard, and the platform wakes your project's backend at the right
9
- times — your code only decides what each job does.
10
-
11
- > **Note:** Schedules need a project with a custom backend. Per-project backends run on the **main** site and
12
- > the **GCP pilot** site. On sites without backend support the Schedules section doesn't appear in
13
- > Settings, and the API answers `not_supported_on_target`.
14
-
15
- ## Defining a schedule
16
-
17
- In the dashboard, open the project's **Settings → Schedules**. Each schedule
18
- has:
19
-
20
- - a **name** (like `daily-digest`) — this is what your code switches on;
21
- - a **cron expression** — standard 5-field cron, always in **UTC**;
22
- - an **enabled** toggle, so you can pause a job without deleting it.
23
-
24
- A project can have up to **5 schedules**, and the shortest interval is one
25
- minute. Each row also shows its last run and, if it failed, the error — so
26
- you can see at a glance whether a job is healthy.
27
-
28
- You can also trigger a schedule manually from the dashboard, which is the
29
- quickest way to test a handler without waiting for the clock.
30
-
31
- ## Handling schedules in your code
32
-
33
- Your project's worker exports a `scheduled` handler next to its normal
34
- `fetch` handler, and switches on the job's name:
35
-
36
- ```ts
37
- export default {
38
- async fetch(request, env, ctx) {
39
- return app.fetch(request, env, ctx);
40
- },
41
-
42
- async scheduled(event, env, ctx) {
43
- switch (event.name) {
44
- case "daily-digest":
45
- await sendDigest(env);
46
- break;
47
- default:
48
- console.warn("unknown schedule", event.name);
49
- }
50
- },
51
- };
52
- ```
53
-
54
- `event.name` is the name you gave the schedule in the dashboard, `event.cron`
55
- is the expression that fired, and `event.scheduledTime` is when it fired.
56
- Inside the handler your code has the same environment as an HTTP request —
57
- secrets, the project database, and blob storage all work the same way.
58
-
59
- ## Data access without a signed-in user
60
-
61
- A scheduled run has no browser session attached, but `canopy.db`, `canopy.blob`
62
- and `canopy.ai` still work: the platform seals a **project-scoped worker
63
- token** into your backend on every deploy, and calls through the platform
64
- fall back to it whenever the request carries no user credential. You don't
65
- configure or see this token — it's minted, rotated (every deploy replaces
66
- it), and revoked with the project automatically, and it only ever reaches
67
- your own project's data.
68
-
69
- The one thing that behaves differently is identity: `canopy.auth.user()`
70
- resolves with `kind: "worker"` and empty `id`/`email`/`name` — there is no
71
- one signed in during a cron tick, and your code should treat that as "no
72
- user", not as an error.
73
-
74
- ## Why the name matters
75
-
76
- Switch on `event.name`, not on the cron string. The dashboard is the source
77
- of truth for _when_ a job runs, so you can change `daily-digest` from 6:00 to
78
- 7:30 — or pause it — without touching code or redeploying. The name is the
79
- stable contract between the dashboard and your handler.
80
-
81
- ## Things to know
82
-
83
- - **Cron runs as the project, not as a person.** Data access works (see
84
- above), but there is no signed-in user — `auth.user()` answers with
85
- `kind: "worker"` and no identity.
86
- - **All times are UTC.** `0 6 * * *` is 6:00 UTC, not local time.
87
- - **Failures are visible.** A run that throws records its error on the
88
- schedule row in Settings, so silent breakage doesn't stay silent.
89
-
90
- - [Infrastructure](https://docs.canopy.website/guides/infrastructure) — What runs where — assets, backends, databases — across the Canopy sites.
@@ -1,61 +0,0 @@
1
- ---
2
- title: Sites & profiles
3
- description: Canopy runs as more than one installation. Most people only ever use main — here's what the others are and how the CLI keeps them straight.
4
- ---
5
-
6
- Canopy isn't a single installation: the same platform runs as a few separate
7
- **sites**, each with its own domain, its own projects, and its own data.
8
- Unless someone told you otherwise, **you're on `main` and can stop reading
9
- here** — the rest of this page explains what the other sites are for and how
10
- the CLI targets them.
11
-
12
- ## The sites
13
-
14
- | Site | Project URLs | What it's for |
15
- | ----------- | --------------------- | ------------------------------------------------- |
16
- | `main` | `*.canopy.website` | The real platform. Your projects live here. |
17
- | `staging` | `*.thanarie.dev` | The platform team's testbed for platform changes. |
18
- | `gcp-pilot` | `*.canopy.monks.zone` | A pilot running on Google Cloud. |
19
-
20
- Sites are fully separate: a project on `main` doesn't exist on `staging`, and
21
- signing in to one doesn't create anything on another. They can also differ in
22
- what they support — the dashboard simply hides features a site can't honor,
23
- and the API says `not_supported_on_target` if asked directly.
24
-
25
- ## What a profile is
26
-
27
- A **profile** is the CLI's name for a site. `canopy login` signs you in once
28
- via OneLogin and stores a credential for every site in one go; the profile
29
- just picks which one a command talks to.
30
-
31
- Three things decide the effective profile, in order:
32
-
33
- 1. `--profile <id>` on the command (or the `CANOPY_PROFILE` environment
34
- variable) — one-off override.
35
- 2. The `"profile"` pinned in the project's `canopy.json` — set when the
36
- project was created, so a checkout always targets the site it belongs to.
37
- 3. Your default active profile — `main` unless you chose otherwise at login.
38
-
39
- Every command prints the site it's talking to in its header line, and
40
- `canopy whoami` shows your email, active profile, and API host — so there's
41
- never a mystery about where a deploy just went.
42
-
43
- ```bash
44
- canopy whoami # who am I, on which site?
45
- canopy --profile gcp-pilot deploy # one command against the pilot
46
- ```
47
-
48
- ## When you'd actually care
49
-
50
- - **Never, usually.** Day-to-day work on your own projects is all `main`,
51
- and the `canopy.json` pin keeps it that way without any flags.
52
- - **Trying backend features.** Custom backends and
53
- [schedules](https://docs.canopy.website/platform/schedules) currently run on `gcp-pilot`, so
54
- experiments with those happen there.
55
- - **Helping test platform changes.** If the platform team asks you to check
56
- something on `staging`, `--profile staging` points any command at it.
57
-
58
- > **Note:** Deploying to a different site publishes a **separate copy** of the project there — it doesn't move
59
- > or mirror the one on `main`. Each site keeps its own versions, data, and members.
60
-
61
- - [CLI reference](https://docs.canopy.website/guides/cli) — Every canopy command, including login, whoami, and the --profile flag.
@@ -1,233 +0,0 @@
1
- ---
2
- title: Porting a Next.js app
3
- description: How to restructure a Next.js (App Router) project so it runs on Canopy.
4
- ---
5
-
6
- Canopy can host a project that started life as a Next.js app — but not by
7
- running Next.js's server. This guide explains why, and walks through the
8
- restructuring: a **static frontend** plus an optional **backend worker on
9
- `/api/*`**, with the platform taking over auth, data, files, and AI.
10
-
11
- If your app barely uses Next's server side, the port is a day or two of work.
12
- If it leans heavily on server components, server actions, or ISR, read
13
- [When not to port](#when-not-to-port) first.
14
-
15
- ## Why a lift-and-shift doesn't work
16
-
17
- Canopy deliberately hosts no arbitrary servers — that's what keeps a thousand
18
- idle projects free to run and safe to operate. Three consequences matter for
19
- Next.js:
20
-
21
- - **Every page is a static file.** The platform serves your deployed build
22
- from storage; requests to `/`, `/about`, or any other page route never reach
23
- project code (see [How static files are served](https://docs.canopy.website/guides/static-serving)).
24
- Server-side rendering, React Server Components resolved per request,
25
- and Incremental Static Regeneration have nowhere to run.
26
- - **The backend is one worker, reachable only under `/api/*`.** A project can
27
- ship a single bundled module exporting `fetch()` (and optionally
28
- `scheduled()`), and the platform routes `/api/*` requests to it. Server
29
- actions — which POST to page routes — never reach it.
30
- - **Auth belongs to the platform.** Every request is already behind OneLogin
31
- (or a project password) before your code runs. NextAuth, auth middleware,
32
- and session handling come out of the ported app entirely.
33
-
34
- So the port is a restructure: the frontend becomes a static build, and
35
- whatever genuinely needs a server moves into the worker.
36
-
37
- ## What a ported app looks like
38
-
39
- ```
40
- my-app/
41
- ├── canopy.json ← slug, build command, dist folder, worker entry
42
- ├── src/ ← frontend (static build)
43
- ├── worker/
44
- │ └── index.ts ← backend: fetch handler for /api/*
45
- └── dist/ ← build output that gets deployed
46
- ```
47
-
48
- ```json
49
- {
50
- "slug": "my-app",
51
- "build": "npm run build",
52
- "dist": "dist",
53
- "worker": "worker/index.ts"
54
- }
55
- ```
56
-
57
- One `canopy deploy` builds and ships both; frontend and backend always move
58
- together as a single version, with rollback.
59
-
60
- ## Step 1 — take inventory
61
-
62
- List everything in the app that runs on the server. Each item lands in one of
63
- three buckets:
64
-
65
- | Next.js feature | Where it goes on Canopy |
66
- | ----------------------------------------------- | -------------------------------------------------------------------------------- |
67
- | Pages, layouts, client components | Static build (unchanged in spirit) |
68
- | Server components fetching data at request time | Client-side fetching, or a worker endpoint |
69
- | Route handlers (`app/api/*/route.ts`) | Worker endpoints under `/api/*` |
70
- | Server actions | Explicit worker endpoints, called with `fetch` from the client |
71
- | Middleware | Usually deleted (auth is platform-provided); anything else moves into the worker |
72
- | NextAuth / auth middleware | Deleted — use `canopy.auth.user()` (client) or `env.USER` (worker) |
73
- | Database (Prisma, Postgres, …) | `canopy.db` — per-project database, document API + SQL escape hatch |
74
- | File uploads (S3, …) | `canopy.blob` — per-project file storage with signed downloads |
75
- | AI SDK / OpenAI client | `canopy.ai.chat()`, or point the Vercel AI SDK at `canopy.ai.endpoint()` |
76
- | `NEXT_PUBLIC_*` env vars | Build-time env vars in your CI — the platform doesn't inject build config |
77
- | Server-side env vars / secrets | Project secrets (dashboard Settings) — injected into the worker's `env` |
78
- | Cron (`vercel.json` crons) | [Schedules](https://docs.canopy.website/platform/schedules) — delivered to the worker's `scheduled()` |
79
- | ISR / `revalidate` / caching | Dropped — at internal-tool scale, fetch fresh or cache in the client |
80
- | `next/image` optimization | Plain `<img>`, or optimize at build time |
81
- | Custom domain | Not available — projects live at `<slug>.canopy.<domain>` behind sign-in |
82
- | Queues / background jobs | Not supported — restructure around request/response plus schedules |
83
-
84
- If the last two rows are deal-breakers, stop here: see
85
- [When not to port](#when-not-to-port).
86
-
87
- ## Step 2 — make the frontend static
88
-
89
- Two routes, pick one:
90
-
91
- **Static export (quickest).** Set `output: 'export'` in `next.config.js` and
92
- fix what breaks. You keep the App Router file layout and components, but lose
93
- everything request-time: no route handlers, no server actions, no dynamic
94
- route segments without `generateStaticParams`, no request-time server
95
- components (they still run — but once, at build time). `next build` writes a
96
- fully static site to `out/`; point `dist` at it. Set
97
- `images: { unoptimized: true }` since there's no image optimizer serving
98
- requests.
99
-
100
- **Rebuild as a Vite SPA (more idiomatic).** Canopy's templates and tooling
101
- assume a single-page app: one `index.html`, client-side routing, `canopy dev`
102
- for local development against real project data. If you're porting an app
103
- you'll keep evolving on Canopy — especially with an AI agent doing the work —
104
- converting pages to a React + Vite SPA usually pays for itself quickly, and
105
- agents handle this conversion well. Page routes become client routes (the
106
- platform serves `index.html` for unknown paths, so deep links work).
107
-
108
- Either way, data that pages used to fetch on the server now arrives one of two
109
- ways: fetched from the client through the SDK (`canopy.db`, `canopy.blob` —
110
- fine for most internal tools), or from your worker via `/api/*` when it needs
111
- server-side logic or secrets.
112
-
113
- ## Step 3 — move the backend into a worker
114
-
115
- Route handlers and server actions become endpoints in one fetch handler.
116
- [Hono](https://hono.dev) is a natural fit and bundles cleanly:
117
-
118
- ```ts
119
- // worker/index.ts
120
- import { createCanopy } from "@mediamonks/canopy";
121
- import { Hono } from "hono";
122
-
123
- type Env = {
124
- CANOPY: { fetch: typeof fetch };
125
- /** Signed-in monk, verified by the platform; null for cron. */
126
- USER: { sub: string; email: string; name: string } | null;
127
- /** Project secrets from dashboard Settings appear as plain keys. */
128
- MY_API_KEY?: string;
129
- };
130
-
131
- const app = new Hono<{ Bindings: Env }>();
132
-
133
- // was: app/api/report/route.ts
134
- app.post("/api/report", async (c) => {
135
- const user = c.env.USER;
136
- if (!user) return c.json({ error: "unauthenticated" }, 401);
137
-
138
- // The SDK works inside the worker through the CANOPY binding —
139
- // same database and files the frontend sees, caller's identity attached.
140
- const canopy = createCanopy({ fetch: c.env.CANOPY.fetch.bind(c.env.CANOPY) });
141
- const rows = await canopy.db.collection("reports").find({ where: { owner: user.email } });
142
- return c.json({ rows });
143
- });
144
-
145
- // was: a server action
146
- app.post("/api/submit", async (c) => {
147
- const body = await c.req.json();
148
- // …validate, write via canopy.db, call an external API with c.env.MY_API_KEY…
149
- return c.json({ ok: true });
150
- });
151
-
152
- export default {
153
- fetch: app.fetch.bind(app),
154
- // was: vercel.json crons — configure in dashboard Settings → Schedules
155
- async scheduled(event: { name: string }, env: Env) {
156
- if (event.name === "nightly-report") {
157
- // …
158
- }
159
- },
160
- };
161
- ```
162
-
163
- Rules of the road for the worker:
164
-
165
- - It's **one bundled file** — dependencies are bundled in at deploy time, so
166
- stick to packages that don't need Node built-ins or native modules.
167
- - **No filesystem, no long-lived state.** Persist through `canopy.db` and
168
- `canopy.blob`.
169
- - **Outbound requests are controlled.** Calls to external APIs pass through a
170
- per-project egress allowlist — deny-by-default, managed by you. Allow the
171
- hosts your worker calls with `canopy egress allow <host>` or in dashboard
172
- Settings → Outbound hosts (see [Outbound requests](https://docs.canopy.website/platform/egress)).
173
- - Frontend calls are plain `fetch("/api/…")` — same origin, the platform
174
- session cookie rides along automatically.
175
-
176
- ## Step 4 — swap the data layer
177
-
178
- Replace the ORM and connection strings with the project's own database:
179
-
180
- - The paved road is the [document API](https://docs.canopy.website/sdk/database):
181
- `canopy.db.collection("todos").insert(…)` / `find(…)` — schemaless JSON, no
182
- migrations. For most internal tools this replaces Prisma models outright.
183
- - Relational shapes port via the SQL escape hatch, `canopy.db.sql()`, against
184
- the same per-project database.
185
- - Existing data can be imported once: export from your old database, then load
186
- it through the worker or the [SQL console](https://docs.canopy.website/guides/data) in the dashboard.
187
-
188
- Files move to [`canopy.blob`](https://docs.canopy.website/sdk/files) (uploads, listings, short-lived
189
- signed download URLs — nothing is ever publicly readable). AI calls move to
190
- [`canopy.ai`](https://docs.canopy.website/sdk/ai); if the app uses the Vercel AI SDK, point it at
191
- `canopy.ai.endpoint()` and it keeps working, now metered and budget-capped.
192
-
193
- ## Step 5 — delete the auth
194
-
195
- This is the satisfying part. OneLogin fronts every request before your code
196
- runs, so the ported app removes sign-in pages, session handling, and auth
197
- middleware entirely:
198
-
199
- - **Client:** `canopy.auth.user()` returns the signed-in monk.
200
- - **Worker:** `env.USER` carries the verified identity (no round-trip).
201
- - Client-facing access without OneLogin is a per-project
202
- [password](https://docs.canopy.website/platform/client-access), configured in Settings — not code.
203
-
204
- Authorization (who may do what _inside_ the app) stays yours, but it now keys
205
- off a verified identity instead of a homegrown session.
206
-
207
- ## When not to port
208
-
209
- Be honest about fit before starting. Dedicated hosting is the better home
210
- when the app:
211
-
212
- - depends on **per-request SSR** for real reasons — SEO on public pages,
213
- personalization at the edge, streaming server components as a core UX;
214
- - is built around **server actions and RSC data flow** so thoroughly that the
215
- restructure is a rewrite;
216
- - needs a **custom domain or anonymous public access** — nothing on Canopy is
217
- reachable without OneLogin or a project password;
218
- - needs **queues, background jobs, or long-running work**.
219
-
220
- That's not a workaround waiting to happen — it's the platform's design. Canopy
221
- optimizes for small internal tools; an app that genuinely needs a server is
222
- past greenhouse size (see [Use cases](https://docs.canopy.website/overview/use-cases)).
223
-
224
- ## Checklist
225
-
226
- - [ ] Inventory server-side surface (RSC fetching, actions, route handlers, middleware, crons)
227
- - [ ] Frontend: `output: 'export'` or Vite SPA; fix dynamic segments and `next/image`
228
- - [ ] Worker: route handlers + server actions → `/api/*` endpoints in `worker/index.ts`
229
- - [ ] Data: ORM → `canopy.db`; uploads → `canopy.blob`; AI clients → `canopy.ai`
230
- - [ ] Delete NextAuth/middleware; read identity from `canopy.auth.user()` / `env.USER`
231
- - [ ] Secrets into dashboard Settings; `NEXT_PUBLIC_*` into your CI build
232
- - [ ] Crons into Settings → Schedules, handled in `scheduled()`
233
- - [ ] `canopy.json` with `build`, `dist`, `worker` — then `canopy deploy`