@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.
- package/dist/{deploy-BCtEY1is.mjs → deploy-B1WrPq5W.mjs} +8 -1
- package/dist/{dev-DtoZrexp.mjs → dev-BnUYGQQm.mjs} +5 -4
- package/dist/dev-CySBxW7I.mjs +2 -0
- package/dist/{dev-worker-BZgtEM9V.mjs → dev-worker-IWnHvQ1f.mjs} +42 -7
- package/dist/{docs-BV9qhLJx.mjs → docs-DxB7NhZs.mjs} +1 -1
- package/dist/{feedback-Cb8ccQ_R.mjs → feedback-B-GXUBPs.mjs} +1 -1
- package/dist/index.mjs +10 -10
- package/dist/{local-C5AhA4GM.mjs → local-BGRd_FMZ.mjs} +6 -469
- package/dist/{package-BBpw-n7k.mjs → package-CDLufecy.mjs} +1 -1
- package/dist/{skill-DJHlTEhS.mjs → skill-BOWfqwr2.mjs} +1 -1
- package/dist/src-Bu4Ze32I.mjs +470 -0
- package/dist/{upgrade-B86Ao6T5.mjs → upgrade-fhfrid_O.mjs} +1 -1
- package/package.json +3 -3
- package/dist/dev-BYxSHUH6.mjs +0 -2
- package/dist/docs/ci.md +0 -50
- package/dist/docs/cli-create.md +0 -46
- package/dist/docs/cli-db.md +0 -86
- package/dist/docs/cli-delete.md +0 -44
- package/dist/docs/cli-deploy.md +0 -58
- package/dist/docs/cli-dev.md +0 -82
- package/dist/docs/cli-docs.md +0 -47
- package/dist/docs/cli-download.md +0 -42
- package/dist/docs/cli-egress.md +0 -37
- package/dist/docs/cli-feedback.md +0 -75
- package/dist/docs/cli-index.md +0 -61
- package/dist/docs/cli-init.md +0 -46
- package/dist/docs/cli-login.md +0 -43
- package/dist/docs/cli-logout.md +0 -32
- package/dist/docs/cli-logs.md +0 -57
- package/dist/docs/cli-open.md +0 -41
- package/dist/docs/cli-projects.md +0 -43
- package/dist/docs/cli-promote.md +0 -44
- package/dist/docs/cli-rollback.md +0 -45
- package/dist/docs/cli-skill.md +0 -39
- package/dist/docs/cli-staging.md +0 -54
- package/dist/docs/cli-token.md +0 -47
- package/dist/docs/cli-upgrade.md +0 -43
- package/dist/docs/cli-usage.md +0 -43
- package/dist/docs/cli-whoami.md +0 -46
- package/dist/docs/cli.md +0 -86
- package/dist/docs/data.md +0 -85
- package/dist/docs/deploy.md +0 -81
- package/dist/docs/dev.md +0 -72
- package/dist/docs/developers-agents-and-ai.md +0 -95
- package/dist/docs/developers-architecture.md +0 -106
- package/dist/docs/getting-started.md +0 -108
- package/dist/docs/index.json +0 -331
- package/dist/docs/infrastructure.md +0 -406
- package/dist/docs/migrations.md +0 -183
- package/dist/docs/overview-faq.md +0 -105
- package/dist/docs/overview-how-it-works.md +0 -82
- package/dist/docs/overview-use-cases.md +0 -76
- package/dist/docs/overview-what-is-canopy.md +0 -75
- package/dist/docs/platform-ai-usage.md +0 -65
- package/dist/docs/platform-client-access.md +0 -54
- package/dist/docs/platform-deploys-and-rollbacks.md +0 -93
- package/dist/docs/platform-egress.md +0 -75
- package/dist/docs/platform-feedback.md +0 -114
- package/dist/docs/platform-members-and-access.md +0 -71
- package/dist/docs/platform-project-data.md +0 -76
- package/dist/docs/platform-schedules.md +0 -90
- package/dist/docs/platform-sites-and-profiles.md +0 -61
- package/dist/docs/porting-nextjs.md +0 -233
- package/dist/docs/sdk-ai.md +0 -103
- package/dist/docs/sdk-auth.md +0 -57
- package/dist/docs/sdk-database.md +0 -126
- package/dist/docs/sdk-files.md +0 -82
- package/dist/docs/sdk-services.md +0 -58
- package/dist/docs/sdk.md +0 -126
- package/dist/docs/staging.md +0 -106
- package/dist/docs/static-serving.md +0 -318
- package/dist/docs/troubleshooting.md +0 -102
- package/dist/skill/canopy/SKILL.md +0 -160
- package/dist/templates/spa/.github/workflows/canopy-deploy.yml +0 -34
- package/dist/templates/spa/AGENTS.md +0 -47
- package/dist/templates/spa/README.md +0 -14
- package/dist/templates/spa/_gitignore +0 -4
- package/dist/templates/spa/canopy.json +0 -5
- package/dist/templates/spa/index.html +0 -12
- package/dist/templates/spa/package.json +0 -21
- package/dist/templates/spa/src/app.tsx +0 -25
- package/dist/templates/spa/src/canopy.ts +0 -5
- package/dist/templates/spa/src/main.tsx +0 -10
- package/dist/templates/spa/src/style.css +0 -29
- package/dist/templates/spa/tsconfig.json +0 -16
- package/dist/templates/spa/vite.config.ts +0 -7
- package/dist/templates/static/AGENTS.md +0 -45
- package/dist/templates/static/README.md +0 -9
- package/dist/templates/static/_gitignore +0 -2
- package/dist/templates/static/canopy.json +0 -4
- package/dist/templates/static/index.html +0 -21
- package/dist/templates/static/main.js +0 -9
- package/dist/templates/static/style.css +0 -29
- package/dist/templates/worker/.dev.vars.example +0 -9
- package/dist/templates/worker/.github/workflows/canopy-deploy.yml +0 -34
- package/dist/templates/worker/AGENTS.md +0 -63
- package/dist/templates/worker/README.md +0 -17
- package/dist/templates/worker/_gitignore +0 -5
- package/dist/templates/worker/canopy.json +0 -6
- package/dist/templates/worker/index.html +0 -12
- package/dist/templates/worker/package.json +0 -22
- package/dist/templates/worker/src/app.tsx +0 -25
- package/dist/templates/worker/src/canopy.ts +0 -5
- package/dist/templates/worker/src/main.tsx +0 -10
- package/dist/templates/worker/src/style.css +0 -29
- package/dist/templates/worker/tsconfig.json +0 -16
- package/dist/templates/worker/vite.config.ts +0 -7
- 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`
|