@mediamonks/canopy-cli 0.7.21 → 0.7.23
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/dev-BFPlhkzp.mjs +2 -0
- package/dist/{dev-CNqx_53I.mjs → dev-XULukupE.mjs} +3 -3
- package/dist/{dev-worker-Bij9OUMk.mjs → dev-worker-BDMjExps.mjs} +35 -1
- package/dist/docs/cli-login.md +2 -0
- package/dist/docs/cli-logout.md +9 -6
- package/dist/docs/index.json +27 -7
- package/dist/docs/platform-ai-usage.md +36 -10
- package/dist/docs/platform-assistant.md +50 -0
- package/dist/docs/platform-client-access.md +5 -2
- package/dist/docs/platform-feedback.md +3 -0
- package/dist/docs/platform-members-and-access.md +29 -6
- package/dist/docs/{notifications.md → platform-notifications.md} +6 -2
- package/dist/docs/platform-security-headers.md +53 -0
- package/dist/docs/platform-slack.md +87 -0
- package/dist/docs/sdk-ai.md +102 -11
- package/dist/docs/sdk-files.md +6 -0
- package/dist/docs/sdk-slack.md +132 -0
- package/dist/{docs-fiC6q9Ce.mjs → docs-BDPL0TI3.mjs} +1 -1
- package/dist/{feedback-CAwUHwWa.mjs → feedback-B3pnUPLf.mjs} +8 -3
- package/dist/index.mjs +10 -10
- package/dist/{local-CsSP4Vpt.mjs → local-BjsuI1IB.mjs} +37 -8
- package/dist/login-By4y5O5-.mjs +336 -0
- package/dist/{package-C_DLlB96.mjs → package-C5rZwlw8.mjs} +1 -1
- package/dist/{skill-CmgMU2Iq.mjs → skill-CgToW_Ac.mjs} +1 -1
- package/dist/{src-BKKAEkbH.mjs → src-BPKfY9IN.mjs} +53 -1
- package/dist/{upgrade-D1j0WV4l.mjs → upgrade-Bl_NJ_m5.mjs} +1 -1
- package/dist/{usage-CRpVLLi6.mjs → usage-C1-cvS2-.mjs} +13 -1
- package/package.json +3 -3
- package/dist/dev-D7v7WK9O.mjs +0 -2
- package/dist/login-DRYaJcfT.mjs +0 -285
|
@@ -37,7 +37,7 @@ async function workerPlugin(flags, slug) {
|
|
|
37
37
|
console.log(`note: canopy.json points "worker" at ${entry}, which does not exist — skipping.`);
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
|
-
const { missingSecretNames, readDevVars, workerDevPlugin } = await import("./dev-worker-
|
|
40
|
+
const { missingSecretNames, readDevVars, workerDevPlugin } = await import("./dev-worker-BDMjExps.mjs");
|
|
41
41
|
const secrets = readDevVars(flags.dir);
|
|
42
42
|
const email = siteConfig().email;
|
|
43
43
|
const plugin = workerDevPlugin({
|
|
@@ -165,7 +165,7 @@ async function devRemote(flags, slug) {
|
|
|
165
165
|
async function devLocal(flags, slug) {
|
|
166
166
|
let dump;
|
|
167
167
|
if (flags.pull) {
|
|
168
|
-
const { fetchProjectDump } = await import("./local-
|
|
168
|
+
const { fetchProjectDump } = await import("./local-BjsuI1IB.mjs");
|
|
169
169
|
console.log(`Pulling a fresh export of ${slug}${flags.staging ? " [staging]" : ""}…`);
|
|
170
170
|
dump = await fetchProjectDump(slug, flags.staging ? "staging" : "live");
|
|
171
171
|
}
|
|
@@ -180,7 +180,7 @@ async function devLocal(flags, slug) {
|
|
|
180
180
|
const reason = err instanceof ApiError ? err.explain().why : String(err);
|
|
181
181
|
console.log(`Note: ai/services will be unavailable — could not reach the platform (${reason}).`);
|
|
182
182
|
}
|
|
183
|
-
const { createSandbox } = await import("./local-
|
|
183
|
+
const { createSandbox } = await import("./local-BjsuI1IB.mjs");
|
|
184
184
|
const sandbox = await createSandbox({
|
|
185
185
|
slug,
|
|
186
186
|
persistDir: join(flags.dir, ".canopy", "local"),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./src-
|
|
1
|
+
import "./src-BPKfY9IN.mjs";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { relative, resolve, sep } from "node:path";
|
|
4
4
|
//#region ../data-plane/src/worker-request.ts
|
|
@@ -25,6 +25,39 @@ const IDENTITY_STAMP_HEADERS = [
|
|
|
25
25
|
"x-canopy-user-email",
|
|
26
26
|
"x-canopy-user-name"
|
|
27
27
|
];
|
|
28
|
+
/** Cookies that authenticate to Canopy itself — the monk session and the client-page session. */
|
|
29
|
+
const CANOPY_COOKIE_NAMES = /* @__PURE__ */ new Set(["canopy_session", "canopy_client"]);
|
|
30
|
+
/**
|
|
31
|
+
* Strip Canopy's own credentials from a request before untrusted project code
|
|
32
|
+
* sees it (R-01).
|
|
33
|
+
*
|
|
34
|
+
* The visitor's session cookie arrives with every request the gateway
|
|
35
|
+
* dispatches, and a bundle that can read it can replay it against the platform
|
|
36
|
+
* as that person — against the control plane, and against other projects' data
|
|
37
|
+
* on the api host. It is a credential, so the host keeps it in closure scope
|
|
38
|
+
* like the raw DB and blob handles, and hands the bundle a redacted request.
|
|
39
|
+
*
|
|
40
|
+
* Redaction is surgical on purpose: an app's own cookies and its own
|
|
41
|
+
* Authorization scheme survive, because those belong to the app. Only
|
|
42
|
+
* `canopy_session` / `canopy_client` and a `cnpy_` bearer are removed.
|
|
43
|
+
*
|
|
44
|
+
* `env.CANOPY.fetch` still authenticates as the visitor — the host passes it
|
|
45
|
+
* the unredacted headers — so nothing about the platform-facing behaviour
|
|
46
|
+
* changes; the credential simply stops being reachable from bundle code.
|
|
47
|
+
*/
|
|
48
|
+
function redactCanopyCredentials(headers) {
|
|
49
|
+
const cookie = headers.get("cookie");
|
|
50
|
+
if (cookie !== null) {
|
|
51
|
+
const kept = cookie.split(";").filter((part) => {
|
|
52
|
+
const name = part.split("=")[0]?.trim() ?? "";
|
|
53
|
+
return name !== "" && !CANOPY_COOKIE_NAMES.has(name);
|
|
54
|
+
}).join(";").trim();
|
|
55
|
+
if (kept === "") headers.delete("cookie");
|
|
56
|
+
else headers.set("cookie", kept);
|
|
57
|
+
}
|
|
58
|
+
if (headers.get("authorization")?.startsWith("Bearer cnpy_")) headers.delete("authorization");
|
|
59
|
+
return headers;
|
|
60
|
+
}
|
|
28
61
|
/**
|
|
29
62
|
* Strip what must never travel upstream and stamp who is calling. Common to
|
|
30
63
|
* every host; says nothing about credentials.
|
|
@@ -331,6 +364,7 @@ function workerDevPlugin(options) {
|
|
|
331
364
|
}));
|
|
332
365
|
return;
|
|
333
366
|
}
|
|
367
|
+
redactCanopyCredentials(request.headers);
|
|
334
368
|
await sendResponse(res, await worker.fetch(request, env));
|
|
335
369
|
})().catch((err) => {
|
|
336
370
|
if (err instanceof Error) server.ssrFixStacktrace(err);
|
package/dist/docs/cli-login.md
CHANGED
|
@@ -24,6 +24,8 @@ canopy login --json # machine-readable status for agents/CI
|
|
|
24
24
|
2. You finish signing in **in the browser** — your password never passes through the CLI. When OneLogin hands control back, the browser shows a small "You're signed in" page and you can return to the terminal. The sign-in waits up to 5 minutes; the hand-back uses local port `8976`, so if another program holds that port the login fails with a clear message.
|
|
25
25
|
3. The CLI stores a token for **every** site (`main`, `staging`, `gcp-pilot`), not just the one you picked, in `~/.canopy/config.json` — readable only by your user account.
|
|
26
26
|
|
|
27
|
+
Each token is labelled with your machine's hostname and expires after 30 days. The dashboard's **Account** page (your avatar → Account) lists every machine you have signed in from and lets you revoke any of them; `canopy logout` revokes this machine's tokens for you.
|
|
28
|
+
|
|
27
29
|
One login therefore covers all sites; `--profile` only decides which one commands target by default. If minting fails for a non-active site you get a warning but login still succeeds; if it fails for your **active** profile the command errors and asks you to re-run.
|
|
28
30
|
|
|
29
31
|
## Examples
|
package/dist/docs/cli-logout.md
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: canopy logout
|
|
3
|
-
description: Sign out of Canopy
|
|
3
|
+
description: Sign out of Canopy — revoke your CLI tokens on every site and forget them locally.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
`canopy logout` signs you out on this machine
|
|
6
|
+
`canopy logout` signs you out on this machine: it revokes the CLI token stored for every site on the platform, then forgets them in `~/.canopy/config.json`. Use it on a shared machine, or when you want a clean slate before logging in as someone else.
|
|
7
7
|
|
|
8
8
|
## Usage
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
canopy logout
|
|
12
|
+
canopy logout --local # forget the tokens here, but leave them valid on the platform
|
|
12
13
|
```
|
|
13
14
|
|
|
14
15
|
## Arguments & flags
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
- `--local` — only remove the tokens from this machine; nothing is called on the server. The tokens stay valid until they expire (30 days after login), and you can still revoke them from the dashboard's **Account** page.
|
|
18
|
+
|
|
19
|
+
Logout is deliberately all-or-nothing: it signs out of **every** site (`main`, `staging`, `gcp-pilot`) at once — there is no per-profile logout, so the global `--profile` flag has no effect here.
|
|
17
20
|
|
|
18
21
|
## Details
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
For each site with a stored token, the command asks the platform to revoke that token, so it stops working everywhere immediately — not just on this machine. Then the file is cleaned up. If a site cannot be reached, the local copy is still removed and the command tells you which tokens it could not revoke; those keep working until they expire, or until you revoke them from the dashboard's **Account** page (your avatar → Account), which lists every machine you have signed in from.
|
|
21
24
|
|
|
22
|
-
If no token was stored to begin with, it prints `Not logged in.` and exits successfully.
|
|
25
|
+
Your active-site choice stays in the file, so the next `canopy login` picks up where you left off. If no token was stored to begin with, it prints `Not logged in.` and exits successfully.
|
|
23
26
|
|
|
24
27
|
## Examples
|
|
25
28
|
|
|
@@ -27,6 +30,6 @@ If no token was stored to begin with, it prints `Not logged in.` and exits succe
|
|
|
27
30
|
canopy logout
|
|
28
31
|
```
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
Revokes all site tokens on the platform and removes them from `~/.canopy/config.json`; subsequent commands that need the API will ask you to run `canopy login`.
|
|
31
34
|
|
|
32
35
|
See also [canopy login](https://docs.canopy.website/cli/login) and [canopy whoami](https://docs.canopy.website/cli/whoami).
|
package/dist/docs/index.json
CHANGED
|
@@ -77,11 +77,6 @@
|
|
|
77
77
|
"title": "CLI command reference",
|
|
78
78
|
"summary": "Every canopy command, its flags, and what it does."
|
|
79
79
|
},
|
|
80
|
-
{
|
|
81
|
-
"slug": "notifications",
|
|
82
|
-
"title": "Slack notifications",
|
|
83
|
-
"summary": "Canopy DMs you about your reports and projects — replies, new reports, blocked fetches, budget hits, failing schedules — and answers questions when you ask."
|
|
84
|
-
},
|
|
85
80
|
{
|
|
86
81
|
"slug": "static-serving",
|
|
87
82
|
"title": "How static files are served",
|
|
@@ -122,13 +117,18 @@
|
|
|
122
117
|
{
|
|
123
118
|
"slug": "platform-ai-usage",
|
|
124
119
|
"title": "AI access & usage",
|
|
125
|
-
"summary": "Projects talk to AI models through the platform — no API keys to manage,
|
|
120
|
+
"summary": "Projects talk to AI models through the platform — no API keys to manage, hard per-project budgets, and full visibility of spend."
|
|
126
121
|
},
|
|
127
122
|
{
|
|
128
123
|
"slug": "platform-google",
|
|
129
124
|
"title": "Google",
|
|
130
125
|
"summary": "Let your project work with Google data — Sheets, Docs, Calendar, Drive, Gmail — without handling any OAuth yourself."
|
|
131
126
|
},
|
|
127
|
+
{
|
|
128
|
+
"slug": "platform-slack",
|
|
129
|
+
"title": "Slack",
|
|
130
|
+
"summary": "Give your project its own Slack bot — Canopy creates and manages the app, your code handles the events."
|
|
131
|
+
},
|
|
132
132
|
{
|
|
133
133
|
"slug": "platform-members-and-access",
|
|
134
134
|
"title": "Members & access",
|
|
@@ -139,11 +139,26 @@
|
|
|
139
139
|
"title": "Feedback & bug reports",
|
|
140
140
|
"summary": "A built-in feedback widget on every project, a ticket-lite inbox behind it, and a CLI that lets agents work the backlog."
|
|
141
141
|
},
|
|
142
|
+
{
|
|
143
|
+
"slug": "platform-notifications",
|
|
144
|
+
"title": "Notifications",
|
|
145
|
+
"summary": "Canopy DMs you on Slack about your reports and projects — replies, new reports, blocked fetches, budget hits, failing schedules."
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"slug": "platform-assistant",
|
|
149
|
+
"title": "The Canopy assistant",
|
|
150
|
+
"summary": "Ask questions about your projects and the platform in plain language — in Slack, or from the button in the corner of the docs and the dashboard."
|
|
151
|
+
},
|
|
142
152
|
{
|
|
143
153
|
"slug": "platform-client-access",
|
|
144
154
|
"title": "Client access",
|
|
145
155
|
"summary": "Share a project with clients who have no Monks account — a shared password unlocks that one project's URL and nothing else."
|
|
146
156
|
},
|
|
157
|
+
{
|
|
158
|
+
"slug": "platform-security-headers",
|
|
159
|
+
"title": "Browser security",
|
|
160
|
+
"summary": "The security headers the platform adds to your app's pages and files, what they block, and the two switches in Settings."
|
|
161
|
+
},
|
|
147
162
|
{
|
|
148
163
|
"slug": "platform-sites-and-profiles",
|
|
149
164
|
"title": "Sites & profiles",
|
|
@@ -174,7 +189,7 @@
|
|
|
174
189
|
{
|
|
175
190
|
"slug": "cli-logout",
|
|
176
191
|
"title": "canopy logout",
|
|
177
|
-
"summary": "Sign out of Canopy
|
|
192
|
+
"summary": "Sign out of Canopy — revoke your CLI tokens on every site and forget them locally."
|
|
178
193
|
},
|
|
179
194
|
{
|
|
180
195
|
"slug": "cli-create",
|
|
@@ -323,6 +338,11 @@
|
|
|
323
338
|
"title": "Google",
|
|
324
339
|
"summary": "Call Google APIs as the project's connected account or as the visitor's own — the platform holds the OAuth."
|
|
325
340
|
},
|
|
341
|
+
{
|
|
342
|
+
"slug": "sdk-slack",
|
|
343
|
+
"title": "Slack",
|
|
344
|
+
"summary": "Handle your bot's events and slash commands in your worker, and post to Slack as the bot — no tokens in project code."
|
|
345
|
+
},
|
|
326
346
|
{
|
|
327
347
|
"slug": "sdk-services",
|
|
328
348
|
"title": "Services",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: AI access & usage
|
|
3
|
-
description: Projects talk to AI models through the platform — no API keys to manage,
|
|
3
|
+
description: Projects talk to AI models through the platform — no API keys to manage, hard per-project budgets, and full visibility of spend.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Canopy projects can call AI models without you ever handling an API key. The
|
|
7
7
|
platform routes every request through its own metered gateway, tracks what
|
|
8
|
-
each project spends, and enforces
|
|
8
|
+
each project spends, and enforces hard budgets — so a runaway loop in
|
|
9
9
|
a vibecoded app becomes a capped bill and a visible chart, not a surprise
|
|
10
10
|
invoice.
|
|
11
11
|
|
|
@@ -26,9 +26,26 @@ const reply = await canopy.ai.chat({
|
|
|
26
26
|
default — ask for a model outside the list and the API tells you what _is_
|
|
27
27
|
available.
|
|
28
28
|
- **Streaming** via `canopy.ai.chatStream()` for chat-style UIs.
|
|
29
|
+
- **Documents and images.** Messages can attach files — reference something
|
|
30
|
+
in the project's [blob storage](https://docs.canopy.website/sdk/blob) by key, or inline base64 — see
|
|
31
|
+
[the AI SDK page](https://docs.canopy.website/sdk/ai). Models that read PDFs and images get the real
|
|
32
|
+
document, not a text extraction.
|
|
29
33
|
- **An OpenAI-compatible endpoint.** `canopy.ai.endpoint()` returns a base
|
|
30
34
|
URL and model list you can plug into ecosystem tools (like the Vercel AI
|
|
31
35
|
SDK) — same key handling, same budget, same tracking.
|
|
36
|
+
- **Provider-native endpoints.** For provider-specific capabilities —
|
|
37
|
+
Anthropic prompt caching and PDF citations, OpenAI's Responses API — point
|
|
38
|
+
an official SDK at the native base URLs from `canopy.ai.endpoint()`. Same
|
|
39
|
+
budget and tracking as everything else; see [the AI SDK page](https://docs.canopy.website/sdk/ai).
|
|
40
|
+
|
|
41
|
+
## Request size
|
|
42
|
+
|
|
43
|
+
A single chat request — prompt, inline base64 attachments, and referenced
|
|
44
|
+
blobs together — can be up to **8 MB**. Base64 makes files about a third
|
|
45
|
+
larger than they are on disk, so that fits roughly a 6 MB document per
|
|
46
|
+
request. Bigger requests answer with a `chat_too_large` error (HTTP 413)
|
|
47
|
+
before anything is sent to a model. Providers also have their own per-file
|
|
48
|
+
limits, which are typically above this.
|
|
32
49
|
|
|
33
50
|
## Your data and the model providers
|
|
34
51
|
|
|
@@ -41,16 +58,25 @@ This applies to every way your project reaches AI — `canopy.ai.chat()`,
|
|
|
41
58
|
streaming, and the OpenAI-compatible endpoint all go through the same gateway
|
|
42
59
|
to the same accounts.
|
|
43
60
|
|
|
44
|
-
##
|
|
61
|
+
## Budgets: a daily limit and a monthly cap
|
|
62
|
+
|
|
63
|
+
Every project has two AI budgets in USD, enforced from the same spend ledger
|
|
64
|
+
the dashboard displays — so what you see and what's enforced can never
|
|
65
|
+
disagree:
|
|
66
|
+
|
|
67
|
+
- **A daily limit** (default $5) catches runaway loops. Cross it and AI
|
|
68
|
+
calls answer `ai_budget_exceeded` until midnight UTC; everything resets
|
|
69
|
+
the next day.
|
|
70
|
+
- **A monthly cap** (default $20) is the real spending ceiling. Cross it and
|
|
71
|
+
AI calls pause until the calendar month rolls over.
|
|
45
72
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
the
|
|
50
|
-
disagree.
|
|
73
|
+
The error names which one tripped. The Usage tab shows spend against both.
|
|
74
|
+
If a project genuinely needs more headroom, a Canopy admin can raise either
|
|
75
|
+
budget from that same tab — ask in the project's feedback or reach out to
|
|
76
|
+
the platform team.
|
|
51
77
|
|
|
52
|
-
> **Note:** Hitting
|
|
53
|
-
> calls pause until the
|
|
78
|
+
> **Note:** Hitting a budget doesn't break the rest of your app — pages, data, and files keep working. Only AI
|
|
79
|
+
> calls pause until the window rolls over.
|
|
54
80
|
|
|
55
81
|
## The Usage tab
|
|
56
82
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: The Canopy assistant
|
|
3
|
+
description: Ask questions about your projects and the platform in plain language — in Slack, or from the button in the corner of the docs and the dashboard.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Canopy has an assistant that answers questions about **your projects** and
|
|
7
|
+
**how the platform works**. It reads the same things you can see — your
|
|
8
|
+
projects, their deploys, reports, AI spend, egress log and schedules — plus
|
|
9
|
+
these docs, and answers with a link to the guide it answered from.
|
|
10
|
+
|
|
11
|
+
It can only read. It never deploys, rolls back, changes settings or touches
|
|
12
|
+
your data; asked to, it points you at the dashboard or the `canopy` CLI.
|
|
13
|
+
|
|
14
|
+
## Where to ask
|
|
15
|
+
|
|
16
|
+
**In the docs and the dashboard.** The floating button in the bottom-right
|
|
17
|
+
corner opens a choice: send feedback, or ask the assistant. Pick the second
|
|
18
|
+
and type your question. The page you're on sets the context, so on a project's
|
|
19
|
+
dashboard page "how many deploys today?" already knows which project you mean.
|
|
20
|
+
|
|
21
|
+
Your conversation survives a reload — come back to the tab an hour later and
|
|
22
|
+
it's still there. **New chat** clears it and starts over. Only one
|
|
23
|
+
conversation is kept, and it lives in your own browser.
|
|
24
|
+
|
|
25
|
+
**In Slack.** DM **Canopy**, or mention **@Canopy** in a channel. Same
|
|
26
|
+
assistant, same answers. In a channel linked to a project it knows which
|
|
27
|
+
project you mean; replying in a report's notification thread, it knows which
|
|
28
|
+
report. See [Notifications](https://docs.canopy.website/platform/notifications).
|
|
29
|
+
|
|
30
|
+
## What it's good at
|
|
31
|
+
|
|
32
|
+
- **"Why is my fetch failing?"** — it checks the egress log and tells you
|
|
33
|
+
which host was blocked, and where to allow it.
|
|
34
|
+
- **"What happened on my project today?"** — deploys, new reports, status
|
|
35
|
+
changes.
|
|
36
|
+
- **"How do I promote staging to live?"** — it looks it up in these docs
|
|
37
|
+
rather than guessing.
|
|
38
|
+
- **"What's the context on this report?"** — the body, the trail, and the
|
|
39
|
+
triage notes you're allowed to see.
|
|
40
|
+
|
|
41
|
+
## What it can't do
|
|
42
|
+
|
|
43
|
+
- **Change anything.** Read-only, by design.
|
|
44
|
+
- **See projects you can't.** Every lookup runs as you: a project you're not
|
|
45
|
+
on simply doesn't resolve.
|
|
46
|
+
- **Answer questions about your app's own content.** It knows Canopy and your
|
|
47
|
+
projects on it — not what's inside them.
|
|
48
|
+
|
|
49
|
+
It runs on a small daily budget shared by everyone. On the rare day that runs
|
|
50
|
+
out, it says so and comes back the next.
|
|
@@ -36,8 +36,11 @@ it** card:
|
|
|
36
36
|
2. Send the client the project URL and the password.
|
|
37
37
|
|
|
38
38
|
To change the password, set a new one — previously handed-out passwords stop
|
|
39
|
-
working
|
|
40
|
-
|
|
39
|
+
working, and every browser that unlocked the project with the old one is
|
|
40
|
+
signed out on its next visit and asked for the new password. To turn client
|
|
41
|
+
access off entirely, remove the password from the same card, or switch the
|
|
42
|
+
mode back to Monks only; either way, visitors are back to Monks-only
|
|
43
|
+
immediately, unlocked browsers included.
|
|
41
44
|
|
|
42
45
|
> **Warning:** A shared password is deliberately lightweight protection — right for work-in-progress previews,
|
|
43
46
|
> wrong for anything sensitive. Anyone who has it can view the app and share it further, so rotate
|
|
@@ -18,6 +18,9 @@ boards.
|
|
|
18
18
|
|
|
19
19
|
- Appears only for **signed-in Monks**. Client-password visitors never see it —
|
|
20
20
|
the platform doesn't even send them the script.
|
|
21
|
+
- On Canopy's own pages (these docs and the dashboard) the same button also
|
|
22
|
+
opens [the assistant](https://docs.canopy.website/platform/assistant); on your project it is a feedback
|
|
23
|
+
widget and nothing else.
|
|
21
24
|
- On by default for every project. Turn it off per project in
|
|
22
25
|
**Settings → Feedback widget**; the change applies on the next page load.
|
|
23
26
|
- Submissions are always identified (your company sign-in), so there's no
|
|
@@ -61,11 +61,34 @@ Turning it off again is the same toggle.
|
|
|
61
61
|
> **Note:** Collaborators-only gates the **live app**. Management access (settings, data, deploys) is always
|
|
62
62
|
> members-only, regardless of this toggle.
|
|
63
63
|
|
|
64
|
-
## Platform admins and the
|
|
65
|
-
|
|
66
|
-
Platform admins get an extra **
|
|
67
|
-
on Canopy — name, email,
|
|
68
|
-
to grant or remove admin
|
|
69
|
-
|
|
64
|
+
## Platform admins and the Admin page
|
|
65
|
+
|
|
66
|
+
Platform admins get an extra **Admin** page in the dashboard. Its **Users**
|
|
67
|
+
tab lists everyone on Canopy — name, email, when they first and last signed
|
|
68
|
+
in — with a one-click way to grant or remove admin and to sign someone out
|
|
69
|
+
everywhere. Its **Sessions** tab lists every live CLI sign-in on the site:
|
|
70
|
+
who, from which machine, and when it was last used, each revocable on the
|
|
71
|
+
spot. Accounts are created automatically on first OneLogin sign-in; there is
|
|
72
|
+
nothing to provision.
|
|
73
|
+
|
|
74
|
+
## Your sign-ins, and signing out everywhere
|
|
75
|
+
|
|
76
|
+
A Canopy sign-in lives in two places: the browser session from OneLogin, and
|
|
77
|
+
the CLI tokens `canopy login` stores on each machine (one per site). Your
|
|
78
|
+
**Account** page — open the avatar menu in the dashboard header — lists the
|
|
79
|
+
machines you have signed in from, with when each token was last used, and lets
|
|
80
|
+
you revoke any of them. Revoking a token is what `canopy logout` does for the
|
|
81
|
+
machine it runs on.
|
|
82
|
+
|
|
83
|
+
**Sign out everywhere** on the same page ends every browser session and every
|
|
84
|
+
CLI sign-in of yours on that site at once, this one included. Reach for it
|
|
85
|
+
when a laptop goes missing or you signed in somewhere you shouldn't have; the
|
|
86
|
+
next request from any of those places meets the sign-in page. Deploy tokens
|
|
87
|
+
used by CI belong to their projects, not to you, and keep working — revoke
|
|
88
|
+
those from the project's Deploys tab if they were on the lost machine too.
|
|
89
|
+
|
|
90
|
+
Platform admins have the same button for any user on the **Admin** page, which
|
|
91
|
+
is how someone who leaves the company is signed out of Canopy before their
|
|
92
|
+
open sessions would have expired on their own.
|
|
70
93
|
|
|
71
94
|
- [Client access](https://docs.canopy.website/platform/client-access) — Share work-in-progress with clients who don't have a Monks account.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
3
|
-
description: Canopy DMs you about your reports and projects — replies, new reports, blocked fetches, budget hits, failing schedules
|
|
2
|
+
title: Notifications
|
|
3
|
+
description: Canopy DMs you on Slack about your reports and projects — replies, new reports, blocked fetches, budget hits, failing schedules.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Canopy sends you Slack DMs when something on the platform needs your
|
|
@@ -51,6 +51,10 @@ for that it points you at the dashboard or the CLI. In a channel that's
|
|
|
51
51
|
linked to a project, it already knows which project you mean; replying in a
|
|
52
52
|
report's notification thread, it knows which report.
|
|
53
53
|
|
|
54
|
+
The same assistant answers from the button in the corner of these docs and
|
|
55
|
+
the dashboard, if you'd rather not switch to Slack — see
|
|
56
|
+
[The Canopy assistant](https://docs.canopy.website/platform/assistant).
|
|
57
|
+
|
|
54
58
|
## If DMs don't arrive
|
|
55
59
|
|
|
56
60
|
The bot finds you by your work email. If your Slack account uses a different
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Browser security
|
|
3
|
+
description: The security headers the platform adds to your app's pages and files, what they block, and the two switches in Settings.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Every project on Canopy shares one sign-in, so a script that manages to run
|
|
7
|
+
inside one app is a script running next to every other app a visitor can open.
|
|
8
|
+
To keep that from mattering, the platform adds a small set of browser security
|
|
9
|
+
headers to the pages and files it serves for you. You don't configure anything
|
|
10
|
+
to get them, and most projects never notice they're there.
|
|
11
|
+
|
|
12
|
+
## What your pages get
|
|
13
|
+
|
|
14
|
+
- **A Content-Security-Policy.** Scripts may load from your own app and from
|
|
15
|
+
any `https://` address, so CDNs and hosted libraries keep working. Inline
|
|
16
|
+
`<script>` blocks that are part of your deployed HTML keep working too — the
|
|
17
|
+
platform reads them at serve time and allows exactly those. What's blocked:
|
|
18
|
+
inline event handlers such as `onclick="…"`, `javascript:` links, plugins
|
|
19
|
+
(`<object>`, `<embed>`), scripts loaded over plain `http://`, and inline
|
|
20
|
+
scripts that were _not_ in your deploy — which is what an injected script
|
|
21
|
+
usually is.
|
|
22
|
+
- **No embedding by other sites.** Your pages can only be shown in an iframe by
|
|
23
|
+
your own app.
|
|
24
|
+
- **A referrer policy and HTTPS pinning.** Other sites see your origin but not
|
|
25
|
+
your full URLs, and browsers remember to always use HTTPS.
|
|
26
|
+
|
|
27
|
+
## The two switches
|
|
28
|
+
|
|
29
|
+
Both live on the project's **Settings → Security** page, and both take effect
|
|
30
|
+
on the next page load — no redeploy.
|
|
31
|
+
|
|
32
|
+
- **Content-Security-Policy** — on by default. Turn it off if your app depends
|
|
33
|
+
on something the policy blocks and you can't change it: an inline `onclick`,
|
|
34
|
+
a `javascript:` link, a library that writes `<script>` tags into the page at
|
|
35
|
+
runtime, or a backend route that renders HTML with its own inline scripts.
|
|
36
|
+
Check the browser console first — a blocked script shows up there with the
|
|
37
|
+
directive that stopped it.
|
|
38
|
+
- **Allow embedding in other sites** — off by default. Turn it on when another
|
|
39
|
+
site needs to show this app in an iframe.
|
|
40
|
+
|
|
41
|
+
Backend-rendered pages (a worker route that returns HTML) receive the framing
|
|
42
|
+
and referrer headers but no script policy, because the platform only hashes
|
|
43
|
+
inline scripts it knows came from a deploy.
|
|
44
|
+
|
|
45
|
+
## Files you store
|
|
46
|
+
|
|
47
|
+
Files saved with `canopy.blob` are served from your app's address, with the
|
|
48
|
+
content type you gave them. Images, video, audio, fonts, plain text, CSV, JSON
|
|
49
|
+
and PDF display normally. SVGs display too, but sandboxed — fine in an
|
|
50
|
+
`<img>`, and harmless if someone opens the URL directly. Anything a browser
|
|
51
|
+
would treat as a page — HTML, XML, scripts, or files with an unknown type —
|
|
52
|
+
downloads instead of rendering. If you want to show user-uploaded HTML, render
|
|
53
|
+
it inside your own page rather than linking to the stored file.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Slack
|
|
3
|
+
description: Give your project its own Slack bot — Canopy creates and manages the app, your code handles the events.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Your project can have its own Slack app: its own name and bot, in whatever
|
|
7
|
+
workspace you choose. Canopy creates the app, keeps its settings in sync,
|
|
8
|
+
holds every credential, and routes Slack's traffic to your backend. Your
|
|
9
|
+
code never sees a bot token, a signing secret, or a webhook — it exports a
|
|
10
|
+
handler and calls `canopy.slack`, both covered in
|
|
11
|
+
[the SDK reference](https://docs.canopy.website/sdk/slack).
|
|
12
|
+
|
|
13
|
+
Everything on this page happens on the project's **Services** tab.
|
|
14
|
+
|
|
15
|
+
## One-time: your configuration token
|
|
16
|
+
|
|
17
|
+
Creating Slack apps requires a Slack _app configuration token_, which is
|
|
18
|
+
personal — it belongs to you, in one workspace. You set it up once and every
|
|
19
|
+
project you manage shares it:
|
|
20
|
+
|
|
21
|
+
1. Go to [api.slack.com/apps](https://api.slack.com/apps) and pick the
|
|
22
|
+
workspace your bots should live in — **that choice is baked into the
|
|
23
|
+
token**, and it's where every app you create will land.
|
|
24
|
+
2. Under "Your App Configuration Tokens", generate a token and copy the
|
|
25
|
+
**refresh token** (the one starting `xoxe-1-`). The access token isn't
|
|
26
|
+
needed.
|
|
27
|
+
3. Paste it into the token section of any project's Services tab and save.
|
|
28
|
+
|
|
29
|
+
Canopy validates it against Slack, stores it encrypted, and keeps it fresh
|
|
30
|
+
from then on — this is genuinely one-time. If it ever stops working (for
|
|
31
|
+
example, it was revoked), existing bots keep running; only app-settings
|
|
32
|
+
changes pause until you paste a fresh one.
|
|
33
|
+
|
|
34
|
+
To create bots in a different workspace — a client's, say — generate a token
|
|
35
|
+
for that workspace and save it. One workspace at a time per person.
|
|
36
|
+
|
|
37
|
+
## Creating the app
|
|
38
|
+
|
|
39
|
+
Name the bot, pick its features, create. Every app comes with a bot that can
|
|
40
|
+
be invited to channels, post messages, and send people direct messages — the
|
|
41
|
+
notification baseline, no options needed. On top of that:
|
|
42
|
+
|
|
43
|
+
- **AI assistant** — people can chat with the bot: by @-mentioning it in
|
|
44
|
+
channels, in direct messages, and in Slack's assistant pane. Without it
|
|
45
|
+
the bot only sends; nobody can talk back. One caveat from Slack's side:
|
|
46
|
+
once on, it can't be turned off again.
|
|
47
|
+
- **Slash commands** — each becomes a workspace command handled by your
|
|
48
|
+
backend. Command names aren't reserved per app, so pick ones another app
|
|
49
|
+
in the workspace isn't already using.
|
|
50
|
+
|
|
51
|
+
## Installing to the workspace
|
|
52
|
+
|
|
53
|
+
Creating the app doesn't put it in the workspace — installing does, and
|
|
54
|
+
that's a human step by design: you approve the bot's permissions on Slack's
|
|
55
|
+
own screen. Click **Install to workspace** and follow it through.
|
|
56
|
+
|
|
57
|
+
If your workspace requires admin approval for new apps, the first attempt
|
|
58
|
+
becomes an approval request instead. Once the admin approves (Slack DMs you
|
|
59
|
+
about it), either come back and click install again, or use the install
|
|
60
|
+
link Slack itself offers — both complete the same way. A Slack-side install
|
|
61
|
+
only works into the workspace the app already belongs to.
|
|
62
|
+
|
|
63
|
+
The same button reappears whenever you change features in a way that needs
|
|
64
|
+
new permissions — the card shows a clear warning when saved changes are
|
|
65
|
+
waiting on a reinstall. Feature changes that don't widen permissions apply
|
|
66
|
+
immediately.
|
|
67
|
+
|
|
68
|
+
## Day to day
|
|
69
|
+
|
|
70
|
+
- **The status line** shows where the app is installed and when Slack last
|
|
71
|
+
delivered something — the quickest "is it wired up" check.
|
|
72
|
+
- **Enabled** is the kill switch: off, Slack traffic is accepted and
|
|
73
|
+
dropped, nothing reaches your code, and outbound sends are refused. The
|
|
74
|
+
app stays installed; flip it back anytime.
|
|
75
|
+
- **Remove Slack app** deletes the app from the workspace too — bot,
|
|
76
|
+
commands, history access, all of it.
|
|
77
|
+
|
|
78
|
+
Every message your project sends through the bot is counted per day, so a
|
|
79
|
+
runaway loop is visible.
|
|
80
|
+
|
|
81
|
+
## Local development
|
|
82
|
+
|
|
83
|
+
Slack needs a public URL to deliver to, so `canopy dev` can't receive
|
|
84
|
+
events or commands. Test inbound handling on a deployed project — a scratch
|
|
85
|
+
project with a scratch bot works well — and unit-test your handler directly.
|
|
86
|
+
Outbound (`canopy.slack.api`) works from `canopy dev` like any other SDK
|
|
87
|
+
call.
|