@north-light/cli 0.3.425 → 0.3.426

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.
@@ -28,16 +28,16 @@ if (run.kind === 'result') console.log(run.output_parsed.root_cause);
28
28
 
29
29
  ## Go live with an app key
30
30
 
31
- Once you have installed the SDK and tried the local flow in [[crouter-sdk/getting-started]], build against production: [[crouter-sdk/guides/apps-on-crouter-cloud]] takes you from signing up at `https://account.northlight.ai` to a running app on your laptop, registering in the directory console. (`crtr cloud apps register` does the same from a machine joined to your account; production does not yet let a laptop join.) On a joined machine, generate the key and pull the settings with:
31
+ Once you have installed the SDK and tried the local flow in [[crouter-sdk/getting-started]], build against production: [[crouter-sdk/guides/apps-on-crouter-cloud]] takes you from signing up at `https://account.northlight.ai` and joining your laptop (`crtr sys join`) to a running app on your laptop, registered with `crtr cloud apps register`. Generate the key and pull the settings with:
32
32
 
33
33
  ```bash
34
34
  npx @north-light/sdk keygen --app <client id>
35
35
  npx @north-light/sdk env --app <client id>
36
36
  ```
37
37
 
38
- `keygen` saves the private JWK to `local/app-key.json` (mode `0600`); keep that file out of git. With `--app`, it offers to add the public key to your registered app through your own `crtr`, as an additional key that never replaces the current one. Otherwise, click **Add key** on the app's **Sign-in** tab and paste the public JWK printed on stdout (or paste it under **Register an app** for a new app). An app authenticates only with its key; the directory issues no client secret.
38
+ `keygen` saves the private JWK to `local/app-key.json` (mode `0600`); keep that file out of git. With `--app`, it offers to add the public key to your registered app through your own `crtr`, as an additional key that never replaces the current one. Otherwise, add the public JWK printed on stdout with `crtr cloud apps keys add --app-id <client id> --jwk '<public JWK>'`. An app authenticates only with its key; the directory issues no client secret.
39
39
 
40
- `env` reads the registration through `crtr cloud apps get` and writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN` into `local/app.env` (`--out` picks another file), keeping every other line. Both commands need this machine's `crtr` joined to your Northlight account; on a machine that isn't, they print a join step and exit 2. On a laptop against production, run `npx @north-light/sdk keygen --out local/app-key.json` without `--app`, paste the public JWK in the console, and write the env file by hand. An app is configured from four variables:
40
+ `env` reads the registration through `crtr cloud apps get` and writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN` into `local/app.env` (`--out` picks another file), keeping every other line. Both commands need this machine's `crtr` joined to your Northlight account; on a machine that isn't, they print a join step and exit 2. An app is configured from four variables:
41
41
 
42
42
  | Variable | Value |
43
43
  |---|---|
@@ -85,7 +85,7 @@ The directory rejects the key until its public JWK is added to the app. To chang
85
85
  | [[crouter-sdk/docker]] | `start()`, `attach()`, `connection()` from `@north-light/env-docker` |
86
86
  | [[crouter-sdk/testing]] | `FakeCloud` from `@north-light/sdk/testing`: a fake directory and runtime your real `OAuth2Client` and `Crouter` talk to through `fetch` |
87
87
  | [[crouter-sdk/release-notes]] | Behavior changes an existing app must act on, newest first |
88
- | [[crouter-sdk/guides/apps-on-crouter-cloud]] | Building and registering an app against production (console registration, keys, env file, scopes, running locally, the `crtr cloud apps` commands); typed results from app runs with `runs.parse`, declines, bounding a run, `run_idle`, and waiting on a provider your app requires |
88
+ | [[crouter-sdk/guides/apps-on-crouter-cloud]] | Building and registering an app against production (joining a laptop, `crtr cloud apps` registration, keys, env file, scopes, running locally, the `crtr cloud apps` commands); typed results from app runs with `runs.parse`, declines, bounding a run, `run_idle`, and waiting on a provider your app requires |
89
89
  | [[crouter-sdk/guides/apps-with-many-users]] | Per-person runtime connections, app-owned user access, privacy, and spending |
90
90
 
91
91
  ## Phases
@@ -22,12 +22,12 @@ That installs the `crtr` CLI and the `crtrd` daemon. You do not have to start th
22
22
  ## 2. Install the SDK in your application
23
23
 
24
24
  ```bash
25
- npm i @north-light/sdk
25
+ npm i --save-exact @north-light/sdk@0.3.422
26
26
  ```
27
27
 
28
28
  One dependency. It re-exports every data type you need, so you do not also install `@north-light/api`.
29
29
 
30
- `@latest` always works against production: a release reaches `latest` only once production serves its contract level. The `next` tag carries every release the moment it is published and may need a newer runtime than production runs (see [[crouter-sdk/errors]] for `RuntimeVersionError`). Pin an exact version in your app.
30
+ `@latest` always works against production: a release reaches `latest` only once production serves its contract level. The `next` tag carries every release the moment it is published and may need a newer runtime than production runs (see [[crouter-sdk/errors]] for `RuntimeVersionError`). Keep the exact pin: the compatibility gate protects the wire, not the TypeScript API. To move it, check `npm view @north-light/sdk dist-tags` and read the [[crouter-sdk/release-notes]] first.
31
31
 
32
32
  ## 3. Node, on the owner's own daemon
33
33
 
@@ -176,7 +176,7 @@ An outcome is returned, never thrown — including a decline and a failure. Only
176
176
 
177
177
  ## Go live with an app key
178
178
 
179
- Build against production from your laptop: [[crouter-sdk/guides/apps-on-crouter-cloud]] walks the whole path. In short, sign up at `https://account.northlight.ai`, register the app in the directory console (`https://account.northlight.ai/console`) with a `http://localhost:<port>` origin and a `http://localhost:<port>/auth/callback` redirect URI while you develop, generate the key with `npx @north-light/sdk keygen --out local/app-key.json` and paste its public JWK, and write `local/app.env` by hand. Production does not yet let a laptop join as an additional machine, so `crtr cloud apps …`, `keygen --app` and `env` work today only on a machine joined to your account; your Northlight chat agent can run the `crtr cloud apps` commands for you ([[crouter-sdk/guides/apps-on-crouter-cloud]]). If you do have a joined machine, generate the key and pull its settings with:
179
+ Build against production from your laptop: [[crouter-sdk/guides/apps-on-crouter-cloud]] walks the whole path. In short, sign up at `https://account.northlight.ai`, create a join token at `https://account.northlight.ai/account/machines` and run `crtr sys join --directory https://account.northlight.ai --token <token>` on your laptop, register the app with `crtr cloud apps register` using a `http://localhost:<port>` origin and a `http://localhost:<port>/auth/callback` redirect URI while you develop, then generate the key and pull its settings with:
180
180
 
181
181
  ```bash
182
182
  npx @north-light/sdk keygen --app <client id>
@@ -187,7 +187,7 @@ npx @north-light/sdk env --app <client id>
187
187
 
188
188
  `env` runs `crtr cloud apps get --app-id <client id>` and writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID`, and `APP_ORIGIN` into `local/app.env` (`--out <path>` writes another file). It updates those three lines in place, keeps every other line and comment, and creates the file with mode `0600` when it doesn't exist. It never writes `CROUTER_KEY_FILE` or anything secret. It prints the values it set and the key file your app will read.
189
189
 
190
- Both commands reach your app through this machine's `crtr`, so the machine must be joined to your Northlight account. On one that isn't, they say so, print a join step and exit 2. Against production that join step does not work yet for a laptop: use `keygen` without `--app` and the console instead.
190
+ Both commands reach your app through this machine's `crtr`, so the machine must be joined to your Northlight account. On one that isn't, they say so, print a join step and exit 2. Against production, [[crouter-sdk/guides/apps-on-crouter-cloud]] first.
191
191
 
192
192
  An app built on the SDK is configured from four variables:
193
193
 
@@ -9,7 +9,7 @@ when-and-why-to-read: When you are building and registering an app on
9
9
 
10
10
  # Apps on crouter cloud
11
11
 
12
- This page first walks through [building and registering an app](#build-and-register-an-app) against production, then covers [managing it with `crtr cloud apps`](#manage-an-app-with-crtr-cloud-apps), then runs.
12
+ This page first walks through [building and registering an app](#build-and-register-an-app) against production with `crtr cloud apps`, then covers [managing it](#manage-an-app-with-crtr-cloud-apps), then runs.
13
13
 
14
14
  An app holding a person's connection starts runs on their runtime with `runs.start`, follows them with `runs.events` or `runs.reply`, and reads them with `runs.get`. This page covers getting data back from a run. For connections and per-person access, see [[crouter-sdk/guides/apps-with-many-users]].
15
15
 
@@ -17,53 +17,100 @@ An app holding a person's connection starts runs on their runtime with `runs.sta
17
17
 
18
18
  This part takes you from nothing to an app running on your laptop against Northlight's production directory, `https://account.northlight.ai`. Your app server runs on `localhost`, you sign in to it through Northlight, and it calls your own runtime. There is no local Northlight stack to run.
19
19
 
20
- You register and manage the app in the directory console, and you write the app's environment file by hand. You do not need `crtr` on your laptop. The `crtr cloud apps …` commands that automate these steps need a machine joined to your Northlight account, and production does not yet let a laptop join as an additional machine. [Manage an app with `crtr cloud apps`](#manage-an-app-with-crtr-cloud-apps) covers the two ways to use them today.
20
+ You do all of it from the command line. Your laptop joins your Northlight account as an extra machine, and `crtr cloud apps …` registers and manages the app as you.
21
21
 
22
22
  The example app texts you, so it asks for the `sms` provider's `sms:send` scope. You need Node.js 22.19 or later.
23
23
 
24
- ### 1. Sign up
24
+ ### 1. Account and join
25
25
 
26
- Open `https://account.northlight.ai/signup` in a fresh browser session (a private window, or a browser profile you have not signed in to Northlight with) and create your account. Signup texts a confirmation code to your phone, and that text is sent over iMessage, so use a phone number that has iMessage. A number that cannot receive iMessage gets "We couldn't send the text", and retrying does not help. Finish the account: the console needs an account with a runtime, and the app you build calls that runtime.
26
+ Open `https://account.northlight.ai/signup` and create your account. Signup texts a confirmation code to your phone, and that text is sent over iMessage, so use a phone number that has iMessage. A number that cannot receive iMessage gets "We couldn't send the text", and retrying does not help. One phone number belongs to one account.
27
27
 
28
- ### 2. Install the SDK
28
+ Install the CLI (`npm i -g @north-light/cli`). Joining needs `crtr` 0.3.422 or later; `crtr --version` shows yours.
29
+
30
+ Then join your laptop to the account:
31
+
32
+ 1. Open `https://account.northlight.ai/account/machines` and choose **Create join token**. A token needs a sign-in within the last 10 minutes, so the page asks you to sign in again if yours is older.
33
+ 2. The page shows two commands, with the token filled in. On your laptop, run them: first `crtr sys daemon stop` (a running daemon makes the join answer `daemon_running`), then `crtr sys join --directory https://account.northlight.ai --token <token>`. Add `--name <label>` to name the machine on the page. The token works once and expires at the time the page shows; the page shows it only once.
34
+
35
+ ```bash
36
+ crtr sys daemon stop
37
+ crtr sys join --directory https://account.northlight.ai --token <token> --name my-laptop
38
+ ```
39
+
40
+ It answers with `registered: true`, the machine id (`runtime:<id>`) and your user id.
41
+ 3. Check it: `crtr cloud apps mine` lists the apps you manage. Before you have joined, it answers `not_joined` and prints these same steps.
42
+
43
+ Things to know before you join:
29
44
 
45
+ - **Joining rewrites your crtr home.** Everything in your everyday crtr home that belonged to the local user (`user:local`) becomes your Northlight user, the next time the daemon starts. If you would rather not do that to your everyday home, join from a home you can throw away.
46
+ - **There is no unjoin.** The only undo is removing the machine on `/account/machines`.
47
+ - **Removing the machine stops it.** Once you choose **Remove** next to a machine, it gets no new tokens at once and the ones it holds expire within 5 minutes. After that its `cloud` commands answer `machine_revoked`: "This machine was removed from your Northlight account." It stays that way until you create a new join token and run `crtr sys join` again. Files on the computer stay on it.
48
+ - **Your hosted machine is unchanged.** `/account/machines` lists it as "Northlight (hosted)", the default. Your chat agent at `https://chat.northlight.ai` still runs `crtr cloud apps …` as before.
49
+
50
+ ### 2. Install the SDK
30
51
 
31
52
  ```bash
32
53
  mkdir my-app && cd my-app
33
54
  npm init -y
34
- npm i @north-light/sdk@latest
55
+ npm i --save-exact @north-light/sdk@0.3.422
35
56
  ```
36
57
 
37
- If a call fails with `426 runtime_version_unsupported`, the SDK is newer than the Northlight runtimes. This can happen briefly right after a new SDK release, until production runtimes are upgraded. Install the previous version (`npm i @north-light/sdk@<previous version>`) or wait, and tell Northlight at support@northlight.ai.
58
+ `0.3.422` is the version this guide was last checked against on production. To move on purpose, run `npm view @north-light/sdk dist-tags`, read the [[crouter-sdk/release-notes]] for the versions in between, and install the new exact version the same way.
38
59
 
39
- ### 3. Generate the signing key
60
+ Pin an exact version of `@north-light/sdk` rather than a range or `latest`. A compatibility gate between the SDK and Northlight's runtimes protects the wire, so an SDK that is too new for the runtime is refused cleanly. It does not protect the TypeScript API: a later SDK release can change a type or a method your code uses, and an unpinned install picks that up on its next `npm i`.
61
+ If a call fails with `426 runtime_version_unsupported`, the SDK is newer than the Northlight runtimes. This can happen briefly right after a new SDK release, until production runtimes are upgraded. Install the previous version or wait, and tell Northlight at support@northlight.ai.
40
62
 
41
- An app signs in with a key, not a client secret. From your app's folder:
63
+ ### 3. Register the app
42
64
 
43
65
  ```bash
44
- npx @north-light/sdk keygen --out local/app-key.json
66
+ crtr cloud apps register \
67
+ --name "Texty" \
68
+ --origin http://localhost:3000 \
69
+ --logo https://example.com/logo.png \
70
+ --justification "Texts you a summary of your day." \
71
+ --redirect-uri http://localhost:3000/auth/callback
45
72
  ```
46
73
 
47
- Run it **without `--app`**: that is the form that needs no `crtr`. It writes the private key to `local/app-key.json` (mode `0600`; keep it out of git) and prints the public JWK, one line of JSON like `{"kty":"EC","x":"…","y":"…","crv":"P-256","kid":"…","alg":"ES256","use":"sig"}`. Keep that line for step 4. Never paste the private key anywhere.
74
+ | Flag | Value |
75
+ |---|---|
76
+ | `--name` | Shown to people on the consent page and in their account. |
77
+ | `--origin` | Where your app runs. `http://localhost:<port>` and `http://127.0.0.1:<port>` are accepted for development. |
78
+ | `--logo` | Required. The URL of a square image. |
79
+ | `--justification` | One sentence, quoted on the consent page as your words. |
80
+ | `--redirect-uri` | Repeat the flag for each URI. Sign-in returns only to a URI listed here, matched exactly, so `localhost` and `127.0.0.1` are different URIs and the port counts. |
48
81
 
49
- ### 4. Register the app in the console
82
+ The answer is the registration: the app id (`app:<client id>`), `verified: false` and `testing: true` (see [Who can sign in: testing mode](#who-can-sign-in-testing-mode)), `key: {"source":"none"}` and an `env` block with `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN`. The client id is the part of the app id after `app:`. `crtr cloud apps get --app-id <client id>` shows it again. Until a key is added, `exchangeCode` throws an `AuthenticationError`.
50
83
 
51
- Open `https://account.northlight.ai/console`, choose **Register an app**, and fill in:
84
+ `--app-id` takes `app:<client id>` or the bare client id everywhere.
52
85
 
53
- | Field | Value |
54
- |---|---|
55
- | Name | Shown to people on the consent page and in their account. |
56
- | Origin URL | Where your app runs, for example `http://localhost:3000`. `http://localhost:<port>` and `http://127.0.0.1:<port>` are accepted for development. |
57
- | Logo URL | Required. A square image. |
58
- | Why your app needs access | One sentence, quoted on the consent page as your words. |
59
- | Redirect URIs | One per line, for example `http://localhost:3000/auth/callback`. Sign-in returns only to a URI listed here, matched exactly, so `localhost` and `127.0.0.1` are different URIs and the port counts. |
60
- | Public key | Paste the public JWK from step 3. It is optional here; if you leave it empty, add it later with **Add key** on the app's **Sign-in** tab. |
86
+ ### 4. Generate the signing key and add it
61
87
 
62
- Choose **Register app**. The app page shows the **Client ID**: copy it. Until the public key is added, `exchangeCode` throws an `AuthenticationError`.
88
+ An app signs in with a key, not a client secret. From your app's folder:
89
+
90
+ ```bash
91
+ npx @north-light/sdk keygen --app <client id> --yes
92
+ ```
93
+
94
+ This writes the private key to `local/app-key.json` (mode `0600`; keep it out of git) and adds its public key to the app through your own `crtr` (`crtr cloud apps keys add`). Without `--yes` it asks first. The answer is "Added key <kid> to <client id> as an additional key. Your current key still works."; `crtr cloud apps keys list --app-id <client id>` then shows the key as `active`. Never paste the private key anywhere.
63
95
 
64
96
  ### 5. Say what the app asks for
65
97
 
66
- On the app page, open **What it asks for**. The app starts with `crtr:llm` and `crtr:act` ticked and can only ask for what is ticked. Tick **Send you text messages from Northlight's number** (under "Tools from providers", scope `sms:send`), then save. Production does not tick it for you, and without it the consent page does not offer it and `providers.call` for `sms` is refused. The consent sentence and the scope string appear together on each row; take scope strings from that page and do not invent them.
98
+ An app can only ask for scopes it has been given. It starts with `crtr:llm` and `crtr:act`. See what else it may ask for, then set the list:
99
+
100
+ ```bash
101
+ crtr cloud apps asks --app-id <client id> --source sms
102
+ crtr cloud apps set-scopes --app-id <client id> --scope crtr:llm --scope crtr:act --scope sms:send
103
+ ```
104
+
105
+ `asks` lists, per source, what the app may ask for and whether it asks now; `sms:send` is under providers, with the consent sentence "Send you text messages from Northlight's number". `set-scopes` replaces the whole list, so repeat `--scope` for every scope you want (`crtr:act` needs `crtr:llm`). Without `sms:send` the consent page does not offer it and `providers.call` for `sms` is refused. Take scope strings from `asks` and do not invent them.
106
+
107
+ If your app does not work without a provider being set up first (Doppel needs Context), name it:
108
+
109
+ ```bash
110
+ crtr cloud apps set-requires --app-id <client id> --provider context
111
+ ```
112
+
113
+ The person is then taken through that provider's setup page the first time they sign in to your app. Context asks for a profile URL, and its state stays `pending` for a few minutes afterwards; see [Waiting on a provider your app requires](#waiting-on-a-provider-your-app-requires).
67
114
 
68
115
  #### What each scope gates
69
116
 
@@ -79,17 +126,19 @@ A run you start can only do what the grant holds and the run's own `scopes` list
79
126
 
80
127
  #### Who can sign in: testing mode
81
128
 
82
- A new app is in testing: it is registered as `verified: false`, only you and up to 100 testers can sign in to it, and it is not shown in the marketplace. You can sign in as yourself with no setup. To let someone else in, add them by the email or phone number on their Northlight account: `crtr cloud apps testers add --app-id <id> --email them@example.com` (or `--phone +15551234567`). An email matches only while it is verified on that account. They can sign in right away; nobody gets a message, so tell them. `testers list` shows who is on the list and `testers remove` takes someone off.
83
-
84
- Anyone else who tries sees "<app> is still in testing", and your app's callback receives `error=access_denied` with `error_description=app_in_testing` instead of a `code`. [[crouter-sdk/client]] shows how to read it and what to show the person. Removing a tester stops their access at once, including their refresh tokens, and keeps their data; adding them back and signing in again restores it.
129
+ A new app is in testing: it is registered as `verified: false`, only you and up to 100 testers can sign in to it, and it is not shown in the marketplace. You can sign in as yourself with no setup. To let someone else in, add them by the email or phone number on their Northlight account: `crtr cloud apps testers add --app-id <id> --email them@example.com` (or `--phone +15551234567`). An email matches only while it is verified on that account. They can sign in right away; nobody gets a message, so tell them. (Specified behavior: adding a tester, and everything below about a second person signing in, being refused or being removed, has not been run on production yet.) `crtr cloud apps testers list --app-id <id>` shows who is on the list and `testers remove` takes someone off. A testing app is also left out of the marketplace listing, even once you set `--listed true`.
85
130
 
86
- The `testers` leaves are `cloud` leaves like the rest, so they need a joined machine or your hosted chat agent (see [Manage an app with `crtr cloud apps`](#manage-an-app-with-crtr-cloud-apps)): a laptop cannot join production yet. Ask your chat agent to run the command, for example `crtr cloud apps testers add --app-id <id> --email them@example.com`.
131
+ Per the testing-mode specification, anyone else who tries sees "<app> is still in testing", and your app's callback receives `error=access_denied` with `error_description=app_in_testing` instead of a `code`. [[crouter-sdk/client]] shows how to read it and what to show the person. Removing a tester stops their access at once, including their refresh tokens, and keeps their data; adding them back and signing in again restores it.
87
132
 
88
133
  When you are ready for everyone, email `support@northlight.ai` to have the app verified. Only Northlight staff can verify an app; registering never sets `verified: true`. Apps that existed before testing mode are already verified.
89
134
 
90
135
  ### 6. Write the environment file
91
136
 
92
- Create `local/app.env` by hand with the Client ID from step 4:
137
+ ```bash
138
+ npx @north-light/sdk env --app <client id>
139
+ ```
140
+
141
+ This reads the registration through `crtr cloud apps get` and writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN` into `local/app.env`, keeping every other line and comment in the file. `--out <path>` writes another file. It never writes `CROUTER_KEY_FILE` or anything secret. Add the key path yourself:
93
142
 
94
143
  ```bash
95
144
  CROUTER_ISSUER=https://account.northlight.ai
@@ -101,11 +150,11 @@ CROUTER_KEY_FILE=local/app-key.json
101
150
  | Variable | Value |
102
151
  |---|---|
103
152
  | `CROUTER_ISSUER` | `https://account.northlight.ai` |
104
- | `CROUTER_CLIENT_ID` | The Client ID from the app page |
105
- | `APP_ORIGIN` | The Origin URL you registered. The redirect URI is `${APP_ORIGIN}/auth/callback` by convention; it must match a registered redirect URI exactly. |
106
- | `CROUTER_KEY_FILE` | Path to the private key from step 3. Default `local/app-key.json` |
153
+ | `CROUTER_CLIENT_ID` | The client id of your app |
154
+ | `APP_ORIGIN` | The origin you registered. The redirect URI is `${APP_ORIGIN}/auth/callback` by convention; it must match a registered redirect URI exactly. |
155
+ | `CROUTER_KEY_FILE` | Path to the private key from step 4. Default `local/app-key.json` |
107
156
 
108
- The file holds no secret, but keep it out of git with the key. On a machine joined to your account, `npx @north-light/sdk env --app <client id>` writes the first three for you.
157
+ The file holds no secret, but keep it out of git with the key.
109
158
 
110
159
  ### 7. Run it locally
111
160
 
@@ -190,12 +239,9 @@ Test your app with `FakeCloud` from `@north-light/sdk/testing`. Your real `OAuth
190
239
 
191
240
  ## Manage an app with `crtr cloud apps`
192
241
 
193
- Everything in the console has a `crtr cloud apps` command: they are leaves of Northlight's `cloud` provider, and they run as you. Two things to know today:
194
-
195
- - **A laptop cannot run them yet.** The `cloud` commands appear on a machine joined to your Northlight account, and production does not yet let a laptop join as an additional machine. That is coming. Until it ships, do the steps above in the console and write the env file by hand. Do not follow older instructions to open `/account/machines` or to run `crtr sys join` against production: that page does not exist yet. `keygen --app` and `npx @north-light/sdk env` need the same joined machine, so use `keygen` without `--app`, as in step 3.
196
- - **Your Northlight chat agent can run them.** Open `https://chat.northlight.ai`, and ask your chat agent to run the command. It runs as you on your hosted runtime and prints the real output. Verified on production: `register`, `set-scopes`, `keys add`, `get`, `mine`, `asks`, `set-listing`, `set-requires` and `update`. For `keys add`, generate the key on your laptop as in step 3 and give the agent only the public JWK. Never ask it to generate your key: it would live on the hosted disk. If a command fails once with `launcher refused launch_failed`, run it again.
242
+ Everything above is a `crtr cloud apps` command, and so is the rest of managing an app. They are leaves of Northlight's `cloud` provider, they run as you, and they need a machine joined to your account ([step 1](#1-account-and-join)).
197
243
 
198
- For example, ask the agent: "Run `crtr cloud apps register --name Texty --origin http://localhost:3000 --logo https://example.com/logo.png --justification "Texts you a summary of your day." --redirect-uri http://localhost:3000/auth/callback` and show me the raw output." Then `set-scopes --app-id <id> --scope crtr:llm --scope crtr:act --scope sms:send` and `keys add --app-id <id> --jwk '<public JWK>'`.
244
+ Your Northlight chat agent at `https://chat.northlight.ai` can run the same commands as you, on your hosted runtime. Ask it to run a command and show you the raw output. It can list and change an app (`mine` and `set-scopes` were checked on production). It cannot delete one: see [Deleting an app](#deleting-an-app). Generate keys on your laptop and give an agent only a public JWK, never the private key.
199
245
 
200
246
  `--app-id` takes `app:<client id>` or the bare client id. The leaves:
201
247
 
@@ -209,24 +255,50 @@ For example, ask the agent: "Run `crtr cloud apps register --name Texty --origin
209
255
  | `crtr cloud apps asks --app-id <id>` | Lists what an app may ask for, with whether it asks now. Large sources such as `integrations` print as a count. Narrow it with `--query sms:send`, `--source sms`, `--group providers\|apps\|agents\|space`, or `--query <text>`; for example `crtr cloud apps asks --app-id <id> --source sms`. |
210
256
  | `crtr cloud apps set-scopes --app-id <id> --scope …` | Replaces the scope list. Repeat `--scope` for every scope; `--clear` empties it; `crtr:act` needs `crtr:llm`. |
211
257
  | `crtr cloud apps set-requires --app-id <id> --provider …` | Names providers your app does not work without (for example `context`); `--clear` empties it. |
212
- | `crtr cloud apps keys add --app-id <id> --jwk '<public JWK>'` | Adds a signing key beside any existing one. Answers `created: false` if that key is already on the app. |
213
- | `crtr cloud apps set-listing --app-id <id> --login-start-url … --text-welcome …` (also `--listed`, `--price`) | Sets the listing fields; pass at least one. A loopback `--login-start-url` is accepted. `--listed true` was not run during this guide's checks. |
214
- | `crtr cloud apps testers add --app-id <id> --email …` (or `--phone …`) | Lets that person sign in while the app is in testing; at most 100. |
258
+ | `crtr cloud apps keys add --app-id <id> --jwk '<public JWK>'` | Adds a signing key beside any existing one. Answers `created: false` if that key is already on the app. `keygen --app` calls it for you. |
259
+ | `crtr cloud apps keys list --app-id <id>` | Lists the keys with their `kid` and `active` or `retired` status. |
260
+ | `crtr cloud apps keys retire --app-id <id> --kid <kid>` | Retires a key so it no longer signs in. |
261
+ | `crtr cloud apps set-listing --app-id <id> --login-start-url … --text-welcome …` (also `--listed`, `--price`) | Sets the listing fields; pass at least one. A loopback `--login-start-url` is accepted. `--listed true --price free` lists the app, so `cloud apps install` finds it; while the app is in testing it still stays out of the marketplace list. |
262
+ | `crtr cloud apps install --app-id <id>` | Texts an install link to the signed-in person. If they already hold a grant it answers `status: already_have` and the install URL instead of texting. The app must be listed. |
263
+ | `crtr cloud apps testers add --app-id <id> --email …` (or `--phone …`) | Lets that person sign in while the app is in testing; at most 100. Not yet run on production. |
215
264
  | `crtr cloud apps testers list --app-id <id>` | Lists the testers. |
216
- | `crtr cloud apps testers remove --app-id <id> --email …` (or `--phone …`) | Removes a tester and revokes their grants for the app. |
265
+ | `crtr cloud apps testers remove --app-id <id> --email …` (or `--phone …`) | Removes a tester and revokes their grants for the app. Not yet run on production. |
217
266
  | `crtr cloud apps delete --app-id <id> --confirm <client id>` | Deletes the app and removes every person's grant for it. Only you at a joined machine's terminal can run it: an agent, including your chat agent, is refused with `provider_refused`. |
218
267
 
219
268
  ### Deleting an app
220
269
 
221
- The console has no control for deleting an app. `crtr cloud apps delete` is the way, and it runs only from a joined machine's terminal, which a laptop cannot be yet. Until then, email `support@northlight.ai` with the client id: Northlight staff can delete an app with an operator command. Deleting removes every person's grant for the app, so each runtime deletes the app's runs, space and schedules, and the app's next refresh for them fails with `invalid_grant`. It cannot be undone, and apps that hold the provider role cannot be deleted. Reusing an app for tests is usually simpler: change its redirect URIs and keys instead.
270
+ ```bash
271
+ crtr cloud apps delete --app-id <client id> --confirm <client id>
272
+ ```
273
+
274
+ It answers `deleted: true` and `removed_grants`, the number of people's grants it removed. Each runtime deletes the app's runs, space and schedules, and the app's next refresh fails with `invalid_grant`. The app's key stops working, the app's page answers 404 and `mine` no longer lists it. It cannot be undone, and apps that hold the provider role cannot be deleted.
275
+
276
+ Only you can delete: run it yourself in the terminal of your joined machine. An agent, including your chat agent, is refused with `provider_refused`: "Only you can delete an app. Ask the person to run `crtr cloud apps delete` in their terminal." Reusing an app for tests is often simpler: change its redirect URIs and keys instead.
222
277
 
223
278
  ### Let people find and install it
224
279
 
225
- `login_start_url` is where Northlight starts sign-in for a person who installs your app from a link. In the console, open **Profile**, and under **Marketplace** set **Sign-in start URL**, **Text welcome** (one or two plain sentences, at most 120 characters, no links) and **Listed in the marketplace**. Your `/auth/login` route from step 7 is a valid start URL. Agents can install an app for people by text only while it is listed.
280
+ `login_start_url` is where Northlight starts sign-in for a person who installs your app from a texted link (specified behavior: pressing the texted link was not run on production, only the text send was). Your `/auth/login` route from step 7 is a valid start URL, and a loopback one is accepted:
281
+
282
+ ```bash
283
+ crtr cloud apps set-listing --app-id <client id> --listed true --price free \
284
+ --login-start-url http://localhost:3000/auth/login \
285
+ --text-welcome "Texts you a summary of your day."
286
+ ```
287
+
288
+ `--text-welcome` is one or two plain sentences, at most 120 characters, with no links. Agents can install an app for people by text only while it is listed (`--listed true`). `crtr cloud apps install --app-id <client id>` sends the signed-in person a text with the install link. Run it only for yourself or for a person who asked: it sends a real text.
226
289
 
227
290
  ### Rotate keys
228
291
 
229
- Add a new key beside the old one, switch, verify with a real sign-in, then retire the old key. Use `keygen --out local/app-key-next.json` without `--app`, paste its public JWK with **Add key** on the **Sign-in** tab, and retire the old one there. [[crouter-sdk/getting-started]] has the full sequence.
292
+ Add a new key beside the old one, switch, verify with a real sign-in, then retire the old key:
293
+
294
+ ```bash
295
+ npx @north-light/sdk keygen --out local/app-key.next.json --app <client id> --yes
296
+ # point CROUTER_KEY_FILE at local/app-key.next.json, restart, and sign in once
297
+ crtr cloud apps keys list --app-id <client id>
298
+ crtr cloud apps keys retire --app-id <client id> --kid <old kid>
299
+ ```
300
+
301
+ The old key keeps working until you retire it, and your app's refreshes keep working after. [[crouter-sdk/getting-started]] has the full sequence.
230
302
 
231
303
  ## Getting a result back from a run
232
304
 
@@ -26,16 +26,16 @@ if (run.kind === 'result') console.log(run.output_parsed.root_cause);
26
26
 
27
27
  ## Go live with an app key
28
28
 
29
- Once you have installed the SDK and tried the local flow in [Getting started](./getting-started.md), build against production: [Build and register an app](./guides/apps-on-crouter-cloud.md#build-and-register-an-app) takes you from signing up at `https://account.northlight.ai` to a running app on your laptop, registering in the directory console. (`crtr cloud apps register` does the same from a machine joined to your account; production does not yet let a laptop join.) On a joined machine, generate the key and pull the settings with:
29
+ Once you have installed the SDK and tried the local flow in [Getting started](./getting-started.md), build against production: [Build and register an app](./guides/apps-on-crouter-cloud.md#build-and-register-an-app) takes you from signing up at `https://account.northlight.ai` and joining your laptop (`crtr sys join`) to a running app on your laptop, registered with `crtr cloud apps register`. Generate the key and pull the settings with:
30
30
 
31
31
  ```bash
32
32
  npx @north-light/sdk keygen --app <client id>
33
33
  npx @north-light/sdk env --app <client id>
34
34
  ```
35
35
 
36
- `keygen` saves the private JWK to `local/app-key.json` (mode `0600`); keep that file out of git. With `--app`, it offers to add the public key to your registered app through your own `crtr`, as an additional key that never replaces the current one. Otherwise, click **Add key** on the app's **Sign-in** tab and paste the public JWK printed on stdout (or paste it under **Register an app** for a new app). An app authenticates only with its key; the directory issues no client secret.
36
+ `keygen` saves the private JWK to `local/app-key.json` (mode `0600`); keep that file out of git. With `--app`, it offers to add the public key to your registered app through your own `crtr`, as an additional key that never replaces the current one. Otherwise, add the public JWK printed on stdout with `crtr cloud apps keys add --app-id <client id> --jwk '<public JWK>'`. An app authenticates only with its key; the directory issues no client secret.
37
37
 
38
- `env` reads the registration through `crtr cloud apps get` and writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN` into `local/app.env` (`--out` picks another file), keeping every other line. Both commands need this machine's `crtr` joined to your Northlight account; on a machine that isn't, they print a join step and exit 2. On a laptop against production, run `npx @north-light/sdk keygen --out local/app-key.json` without `--app`, paste the public JWK in the console, and write the env file by hand. An app is configured from four variables:
38
+ `env` reads the registration through `crtr cloud apps get` and writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN` into `local/app.env` (`--out` picks another file), keeping every other line. Both commands need this machine's `crtr` joined to your Northlight account; on a machine that isn't, they print a join step and exit 2. An app is configured from four variables:
39
39
 
40
40
  | Variable | Value |
41
41
  |---|---|
@@ -83,7 +83,7 @@ The directory rejects the key until its public JWK is added to the app. To chang
83
83
  | [Docker environment](./docker.md) | `start()`, `attach()`, `connection()` from `@north-light/env-docker` |
84
84
  | [Testing your app](./testing.md) | `FakeCloud` from `@north-light/sdk/testing`: a fake directory and runtime your real `OAuth2Client` and `Crouter` talk to through `fetch` |
85
85
  | [Release notes](./release-notes.md) | Behavior changes an existing app must act on, newest first |
86
- | [Apps on crouter cloud](./guides/apps-on-crouter-cloud.md) | Building and registering an app against production (console registration, keys, env file, scopes, running locally, the `crtr cloud apps` commands); typed results from app runs with `runs.parse`, declines, bounding a run, `run_idle`, and waiting on a provider your app requires |
86
+ | [Apps on crouter cloud](./guides/apps-on-crouter-cloud.md) | Building and registering an app against production (joining a laptop, `crtr cloud apps` registration, keys, env file, scopes, running locally, the `crtr cloud apps` commands); typed results from app runs with `runs.parse`, declines, bounding a run, `run_idle`, and waiting on a provider your app requires |
87
87
  | [Apps with many users](./guides/apps-with-many-users.md) | Per-person runtime connections, app-owned user access, privacy, and spending |
88
88
 
89
89
  ## Phases
@@ -20,12 +20,12 @@ That installs the `crtr` CLI and the `crtrd` daemon. You do not have to start th
20
20
  ## 2. Install the SDK in your application
21
21
 
22
22
  ```bash
23
- npm i @north-light/sdk
23
+ npm i --save-exact @north-light/sdk@0.3.422
24
24
  ```
25
25
 
26
26
  One dependency. It re-exports every data type you need, so you do not also install `@north-light/api`.
27
27
 
28
- `@latest` always works against production: a release reaches `latest` only once production serves its contract level. The `next` tag carries every release the moment it is published and may need a newer runtime than production runs (see [Errors](./errors.md) for `RuntimeVersionError`). Pin an exact version in your app.
28
+ `@latest` always works against production: a release reaches `latest` only once production serves its contract level. The `next` tag carries every release the moment it is published and may need a newer runtime than production runs (see [Errors](./errors.md) for `RuntimeVersionError`). Keep the exact pin: the compatibility gate protects the wire, not the TypeScript API. To move it, check `npm view @north-light/sdk dist-tags` and read the [release notes](./release-notes.md) first.
29
29
 
30
30
  ## 3. Node, on the owner's own daemon
31
31
 
@@ -174,7 +174,7 @@ An outcome is returned, never thrown — including a decline and a failure. Only
174
174
 
175
175
  ## Go live with an app key
176
176
 
177
- Build against production from your laptop: [Build and register an app](./guides/apps-on-crouter-cloud.md#build-and-register-an-app) walks the whole path. In short, sign up at `https://account.northlight.ai`, register the app in the directory console (`https://account.northlight.ai/console`) with a `http://localhost:<port>` origin and a `http://localhost:<port>/auth/callback` redirect URI while you develop, generate the key with `npx @north-light/sdk keygen --out local/app-key.json` and paste its public JWK, and write `local/app.env` by hand. Production does not yet let a laptop join as an additional machine, so `crtr cloud apps …`, `keygen --app` and `env` work today only on a machine joined to your account; your Northlight chat agent can run the `crtr cloud apps` commands for you ([how](./guides/apps-on-crouter-cloud.md#manage-an-app-with-crtr-cloud-apps)). If you do have a joined machine, generate the key and pull its settings with:
177
+ Build against production from your laptop: [Build and register an app](./guides/apps-on-crouter-cloud.md#build-and-register-an-app) walks the whole path. In short, sign up at `https://account.northlight.ai`, create a join token at `https://account.northlight.ai/account/machines` and run `crtr sys join --directory https://account.northlight.ai --token <token>` on your laptop, register the app with `crtr cloud apps register` using a `http://localhost:<port>` origin and a `http://localhost:<port>/auth/callback` redirect URI while you develop, then generate the key and pull its settings with:
178
178
 
179
179
  ```bash
180
180
  npx @north-light/sdk keygen --app <client id>
@@ -185,7 +185,7 @@ npx @north-light/sdk env --app <client id>
185
185
 
186
186
  `env` runs `crtr cloud apps get --app-id <client id>` and writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID`, and `APP_ORIGIN` into `local/app.env` (`--out <path>` writes another file). It updates those three lines in place, keeps every other line and comment, and creates the file with mode `0600` when it doesn't exist. It never writes `CROUTER_KEY_FILE` or anything secret. It prints the values it set and the key file your app will read.
187
187
 
188
- Both commands reach your app through this machine's `crtr`, so the machine must be joined to your Northlight account. On one that isn't, they say so, print a join step and exit 2. Against production that join step does not work yet for a laptop: use `keygen` without `--app` and the console instead.
188
+ Both commands reach your app through this machine's `crtr`, so the machine must be joined to your Northlight account. On one that isn't, they say so, print a join step and exit 2. Against production, [join your laptop](./guides/apps-on-crouter-cloud.md#1-account-and-join) first.
189
189
 
190
190
  An app built on the SDK is configured from four variables:
191
191
 
@@ -5,7 +5,7 @@ description: When you are building and registering an app on Northlight, or an a
5
5
 
6
6
  # Apps on crouter cloud
7
7
 
8
- This page first walks through [building and registering an app](#build-and-register-an-app) against production, then covers [managing it with `crtr cloud apps`](#manage-an-app-with-crtr-cloud-apps), then runs.
8
+ This page first walks through [building and registering an app](#build-and-register-an-app) against production with `crtr cloud apps`, then covers [managing it](#manage-an-app-with-crtr-cloud-apps), then runs.
9
9
 
10
10
  An app holding a person's connection starts runs on their runtime with `runs.start`, follows them with `runs.events` or `runs.reply`, and reads them with `runs.get`. This page covers getting data back from a run. For connections and per-person access, see [Apps with many users](./apps-with-many-users.md).
11
11
 
@@ -13,53 +13,100 @@ An app holding a person's connection starts runs on their runtime with `runs.sta
13
13
 
14
14
  This part takes you from nothing to an app running on your laptop against Northlight's production directory, `https://account.northlight.ai`. Your app server runs on `localhost`, you sign in to it through Northlight, and it calls your own runtime. There is no local Northlight stack to run.
15
15
 
16
- You register and manage the app in the directory console, and you write the app's environment file by hand. You do not need `crtr` on your laptop. The `crtr cloud apps …` commands that automate these steps need a machine joined to your Northlight account, and production does not yet let a laptop join as an additional machine. [Manage an app with `crtr cloud apps`](#manage-an-app-with-crtr-cloud-apps) covers the two ways to use them today.
16
+ You do all of it from the command line. Your laptop joins your Northlight account as an extra machine, and `crtr cloud apps …` registers and manages the app as you.
17
17
 
18
18
  The example app texts you, so it asks for the `sms` provider's `sms:send` scope. You need Node.js 22.19 or later.
19
19
 
20
- ### 1. Sign up
20
+ ### 1. Account and join
21
21
 
22
- Open `https://account.northlight.ai/signup` in a fresh browser session (a private window, or a browser profile you have not signed in to Northlight with) and create your account. Signup texts a confirmation code to your phone, and that text is sent over iMessage, so use a phone number that has iMessage. A number that cannot receive iMessage gets "We couldn't send the text", and retrying does not help. Finish the account: the console needs an account with a runtime, and the app you build calls that runtime.
22
+ Open `https://account.northlight.ai/signup` and create your account. Signup texts a confirmation code to your phone, and that text is sent over iMessage, so use a phone number that has iMessage. A number that cannot receive iMessage gets "We couldn't send the text", and retrying does not help. One phone number belongs to one account.
23
23
 
24
- ### 2. Install the SDK
24
+ Install the CLI (`npm i -g @north-light/cli`). Joining needs `crtr` 0.3.422 or later; `crtr --version` shows yours.
25
+
26
+ Then join your laptop to the account:
27
+
28
+ 1. Open `https://account.northlight.ai/account/machines` and choose **Create join token**. A token needs a sign-in within the last 10 minutes, so the page asks you to sign in again if yours is older.
29
+ 2. The page shows two commands, with the token filled in. On your laptop, run them: first `crtr sys daemon stop` (a running daemon makes the join answer `daemon_running`), then `crtr sys join --directory https://account.northlight.ai --token <token>`. Add `--name <label>` to name the machine on the page. The token works once and expires at the time the page shows; the page shows it only once.
30
+
31
+ ```bash
32
+ crtr sys daemon stop
33
+ crtr sys join --directory https://account.northlight.ai --token <token> --name my-laptop
34
+ ```
35
+
36
+ It answers with `registered: true`, the machine id (`runtime:<id>`) and your user id.
37
+ 3. Check it: `crtr cloud apps mine` lists the apps you manage. Before you have joined, it answers `not_joined` and prints these same steps.
38
+
39
+ Things to know before you join:
25
40
 
41
+ - **Joining rewrites your crtr home.** Everything in your everyday crtr home that belonged to the local user (`user:local`) becomes your Northlight user, the next time the daemon starts. If you would rather not do that to your everyday home, join from a home you can throw away.
42
+ - **There is no unjoin.** The only undo is removing the machine on `/account/machines`.
43
+ - **Removing the machine stops it.** Once you choose **Remove** next to a machine, it gets no new tokens at once and the ones it holds expire within 5 minutes. After that its `cloud` commands answer `machine_revoked`: "This machine was removed from your Northlight account." It stays that way until you create a new join token and run `crtr sys join` again. Files on the computer stay on it.
44
+ - **Your hosted machine is unchanged.** `/account/machines` lists it as "Northlight (hosted)", the default. Your chat agent at `https://chat.northlight.ai` still runs `crtr cloud apps …` as before.
45
+
46
+ ### 2. Install the SDK
26
47
 
27
48
  ```bash
28
49
  mkdir my-app && cd my-app
29
50
  npm init -y
30
- npm i @north-light/sdk@latest
51
+ npm i --save-exact @north-light/sdk@0.3.422
31
52
  ```
32
53
 
33
- If a call fails with `426 runtime_version_unsupported`, the SDK is newer than the Northlight runtimes. This can happen briefly right after a new SDK release, until production runtimes are upgraded. Install the previous version (`npm i @north-light/sdk@<previous version>`) or wait, and tell Northlight at support@northlight.ai.
54
+ `0.3.422` is the version this guide was last checked against on production. To move on purpose, run `npm view @north-light/sdk dist-tags`, read the [release notes](../release-notes.md) for the versions in between, and install the new exact version the same way.
34
55
 
35
- ### 3. Generate the signing key
56
+ Pin an exact version of `@north-light/sdk` rather than a range or `latest`. A compatibility gate between the SDK and Northlight's runtimes protects the wire, so an SDK that is too new for the runtime is refused cleanly. It does not protect the TypeScript API: a later SDK release can change a type or a method your code uses, and an unpinned install picks that up on its next `npm i`.
57
+ If a call fails with `426 runtime_version_unsupported`, the SDK is newer than the Northlight runtimes. This can happen briefly right after a new SDK release, until production runtimes are upgraded. Install the previous version or wait, and tell Northlight at support@northlight.ai.
36
58
 
37
- An app signs in with a key, not a client secret. From your app's folder:
59
+ ### 3. Register the app
38
60
 
39
61
  ```bash
40
- npx @north-light/sdk keygen --out local/app-key.json
62
+ crtr cloud apps register \
63
+ --name "Texty" \
64
+ --origin http://localhost:3000 \
65
+ --logo https://example.com/logo.png \
66
+ --justification "Texts you a summary of your day." \
67
+ --redirect-uri http://localhost:3000/auth/callback
41
68
  ```
42
69
 
43
- Run it **without `--app`**: that is the form that needs no `crtr`. It writes the private key to `local/app-key.json` (mode `0600`; keep it out of git) and prints the public JWK, one line of JSON like `{"kty":"EC","x":"…","y":"…","crv":"P-256","kid":"…","alg":"ES256","use":"sig"}`. Keep that line for step 4. Never paste the private key anywhere.
70
+ | Flag | Value |
71
+ |---|---|
72
+ | `--name` | Shown to people on the consent page and in their account. |
73
+ | `--origin` | Where your app runs. `http://localhost:<port>` and `http://127.0.0.1:<port>` are accepted for development. |
74
+ | `--logo` | Required. The URL of a square image. |
75
+ | `--justification` | One sentence, quoted on the consent page as your words. |
76
+ | `--redirect-uri` | Repeat the flag for each URI. Sign-in returns only to a URI listed here, matched exactly, so `localhost` and `127.0.0.1` are different URIs and the port counts. |
44
77
 
45
- ### 4. Register the app in the console
78
+ The answer is the registration: the app id (`app:<client id>`), `verified: false` and `testing: true` (see [Who can sign in: testing mode](#who-can-sign-in-testing-mode)), `key: {"source":"none"}` and an `env` block with `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN`. The client id is the part of the app id after `app:`. `crtr cloud apps get --app-id <client id>` shows it again. Until a key is added, `exchangeCode` throws an `AuthenticationError`.
46
79
 
47
- Open `https://account.northlight.ai/console`, choose **Register an app**, and fill in:
80
+ `--app-id` takes `app:<client id>` or the bare client id everywhere.
48
81
 
49
- | Field | Value |
50
- |---|---|
51
- | Name | Shown to people on the consent page and in their account. |
52
- | Origin URL | Where your app runs, for example `http://localhost:3000`. `http://localhost:<port>` and `http://127.0.0.1:<port>` are accepted for development. |
53
- | Logo URL | Required. A square image. |
54
- | Why your app needs access | One sentence, quoted on the consent page as your words. |
55
- | Redirect URIs | One per line, for example `http://localhost:3000/auth/callback`. Sign-in returns only to a URI listed here, matched exactly, so `localhost` and `127.0.0.1` are different URIs and the port counts. |
56
- | Public key | Paste the public JWK from step 3. It is optional here; if you leave it empty, add it later with **Add key** on the app's **Sign-in** tab. |
82
+ ### 4. Generate the signing key and add it
57
83
 
58
- Choose **Register app**. The app page shows the **Client ID**: copy it. Until the public key is added, `exchangeCode` throws an `AuthenticationError`.
84
+ An app signs in with a key, not a client secret. From your app's folder:
85
+
86
+ ```bash
87
+ npx @north-light/sdk keygen --app <client id> --yes
88
+ ```
89
+
90
+ This writes the private key to `local/app-key.json` (mode `0600`; keep it out of git) and adds its public key to the app through your own `crtr` (`crtr cloud apps keys add`). Without `--yes` it asks first. The answer is "Added key <kid> to <client id> as an additional key. Your current key still works."; `crtr cloud apps keys list --app-id <client id>` then shows the key as `active`. Never paste the private key anywhere.
59
91
 
60
92
  ### 5. Say what the app asks for
61
93
 
62
- On the app page, open **What it asks for**. The app starts with `crtr:llm` and `crtr:act` ticked and can only ask for what is ticked. Tick **Send you text messages from Northlight's number** (under "Tools from providers", scope `sms:send`), then save. Production does not tick it for you, and without it the consent page does not offer it and `providers.call` for `sms` is refused. The consent sentence and the scope string appear together on each row; take scope strings from that page and do not invent them.
94
+ An app can only ask for scopes it has been given. It starts with `crtr:llm` and `crtr:act`. See what else it may ask for, then set the list:
95
+
96
+ ```bash
97
+ crtr cloud apps asks --app-id <client id> --source sms
98
+ crtr cloud apps set-scopes --app-id <client id> --scope crtr:llm --scope crtr:act --scope sms:send
99
+ ```
100
+
101
+ `asks` lists, per source, what the app may ask for and whether it asks now; `sms:send` is under providers, with the consent sentence "Send you text messages from Northlight's number". `set-scopes` replaces the whole list, so repeat `--scope` for every scope you want (`crtr:act` needs `crtr:llm`). Without `sms:send` the consent page does not offer it and `providers.call` for `sms` is refused. Take scope strings from `asks` and do not invent them.
102
+
103
+ If your app does not work without a provider being set up first (Doppel needs Context), name it:
104
+
105
+ ```bash
106
+ crtr cloud apps set-requires --app-id <client id> --provider context
107
+ ```
108
+
109
+ The person is then taken through that provider's setup page the first time they sign in to your app. Context asks for a profile URL, and its state stays `pending` for a few minutes afterwards; see [Waiting on a provider your app requires](#waiting-on-a-provider-your-app-requires).
63
110
 
64
111
  #### What each scope gates
65
112
 
@@ -75,17 +122,19 @@ A run you start can only do what the grant holds and the run's own `scopes` list
75
122
 
76
123
  #### Who can sign in: testing mode
77
124
 
78
- A new app is in testing: it is registered as `verified: false`, only you and up to 100 testers can sign in to it, and it is not shown in the marketplace. You can sign in as yourself with no setup. To let someone else in, add them by the email or phone number on their Northlight account: `crtr cloud apps testers add --app-id <id> --email them@example.com` (or `--phone +15551234567`). An email matches only while it is verified on that account. They can sign in right away; nobody gets a message, so tell them. `testers list` shows who is on the list and `testers remove` takes someone off.
79
-
80
- Anyone else who tries sees "<app> is still in testing", and your app's callback receives `error=access_denied` with `error_description=app_in_testing` instead of a `code`. [When sign-in is refused: apps in testing](../client.md#when-sign-in-is-refused-apps-in-testing) shows how to read it and what to show the person. Removing a tester stops their access at once, including their refresh tokens, and keeps their data; adding them back and signing in again restores it.
125
+ A new app is in testing: it is registered as `verified: false`, only you and up to 100 testers can sign in to it, and it is not shown in the marketplace. You can sign in as yourself with no setup. To let someone else in, add them by the email or phone number on their Northlight account: `crtr cloud apps testers add --app-id <id> --email them@example.com` (or `--phone +15551234567`). An email matches only while it is verified on that account. They can sign in right away; nobody gets a message, so tell them. (Specified behavior: adding a tester, and everything below about a second person signing in, being refused or being removed, has not been run on production yet.) `crtr cloud apps testers list --app-id <id>` shows who is on the list and `testers remove` takes someone off. A testing app is also left out of the marketplace listing, even once you set `--listed true`.
81
126
 
82
- The `testers` leaves are `cloud` leaves like the rest, so they need a joined machine or your hosted chat agent (see [Manage an app with `crtr cloud apps`](#manage-an-app-with-crtr-cloud-apps)): a laptop cannot join production yet. Ask your chat agent to run the command, for example `crtr cloud apps testers add --app-id <id> --email them@example.com`.
127
+ Per the testing-mode specification, anyone else who tries sees "<app> is still in testing", and your app's callback receives `error=access_denied` with `error_description=app_in_testing` instead of a `code`. [When sign-in is refused: apps in testing](../client.md#when-sign-in-is-refused-apps-in-testing) shows how to read it and what to show the person. Removing a tester stops their access at once, including their refresh tokens, and keeps their data; adding them back and signing in again restores it.
83
128
 
84
129
  When you are ready for everyone, email `support@northlight.ai` to have the app verified. Only Northlight staff can verify an app; registering never sets `verified: true`. Apps that existed before testing mode are already verified.
85
130
 
86
131
  ### 6. Write the environment file
87
132
 
88
- Create `local/app.env` by hand with the Client ID from step 4:
133
+ ```bash
134
+ npx @north-light/sdk env --app <client id>
135
+ ```
136
+
137
+ This reads the registration through `crtr cloud apps get` and writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN` into `local/app.env`, keeping every other line and comment in the file. `--out <path>` writes another file. It never writes `CROUTER_KEY_FILE` or anything secret. Add the key path yourself:
89
138
 
90
139
  ```bash
91
140
  CROUTER_ISSUER=https://account.northlight.ai
@@ -97,11 +146,11 @@ CROUTER_KEY_FILE=local/app-key.json
97
146
  | Variable | Value |
98
147
  |---|---|
99
148
  | `CROUTER_ISSUER` | `https://account.northlight.ai` |
100
- | `CROUTER_CLIENT_ID` | The Client ID from the app page |
101
- | `APP_ORIGIN` | The Origin URL you registered. The redirect URI is `${APP_ORIGIN}/auth/callback` by convention; it must match a registered redirect URI exactly. |
102
- | `CROUTER_KEY_FILE` | Path to the private key from step 3. Default `local/app-key.json` |
149
+ | `CROUTER_CLIENT_ID` | The client id of your app |
150
+ | `APP_ORIGIN` | The origin you registered. The redirect URI is `${APP_ORIGIN}/auth/callback` by convention; it must match a registered redirect URI exactly. |
151
+ | `CROUTER_KEY_FILE` | Path to the private key from step 4. Default `local/app-key.json` |
103
152
 
104
- The file holds no secret, but keep it out of git with the key. On a machine joined to your account, `npx @north-light/sdk env --app <client id>` writes the first three for you.
153
+ The file holds no secret, but keep it out of git with the key.
105
154
 
106
155
  ### 7. Run it locally
107
156
 
@@ -186,12 +235,9 @@ Test your app with `FakeCloud` from `@north-light/sdk/testing`. Your real `OAuth
186
235
 
187
236
  ## Manage an app with `crtr cloud apps`
188
237
 
189
- Everything in the console has a `crtr cloud apps` command: they are leaves of Northlight's `cloud` provider, and they run as you. Two things to know today:
190
-
191
- - **A laptop cannot run them yet.** The `cloud` commands appear on a machine joined to your Northlight account, and production does not yet let a laptop join as an additional machine. That is coming. Until it ships, do the steps above in the console and write the env file by hand. Do not follow older instructions to open `/account/machines` or to run `crtr sys join` against production: that page does not exist yet. `keygen --app` and `npx @north-light/sdk env` need the same joined machine, so use `keygen` without `--app`, as in step 3.
192
- - **Your Northlight chat agent can run them.** Open `https://chat.northlight.ai`, and ask your chat agent to run the command. It runs as you on your hosted runtime and prints the real output. Verified on production: `register`, `set-scopes`, `keys add`, `get`, `mine`, `asks`, `set-listing`, `set-requires` and `update`. For `keys add`, generate the key on your laptop as in step 3 and give the agent only the public JWK. Never ask it to generate your key: it would live on the hosted disk. If a command fails once with `launcher refused launch_failed`, run it again.
238
+ Everything above is a `crtr cloud apps` command, and so is the rest of managing an app. They are leaves of Northlight's `cloud` provider, they run as you, and they need a machine joined to your account ([step 1](#1-account-and-join)).
193
239
 
194
- For example, ask the agent: "Run `crtr cloud apps register --name Texty --origin http://localhost:3000 --logo https://example.com/logo.png --justification "Texts you a summary of your day." --redirect-uri http://localhost:3000/auth/callback` and show me the raw output." Then `set-scopes --app-id <id> --scope crtr:llm --scope crtr:act --scope sms:send` and `keys add --app-id <id> --jwk '<public JWK>'`.
240
+ Your Northlight chat agent at `https://chat.northlight.ai` can run the same commands as you, on your hosted runtime. Ask it to run a command and show you the raw output. It can list and change an app (`mine` and `set-scopes` were checked on production). It cannot delete one: see [Deleting an app](#deleting-an-app). Generate keys on your laptop and give an agent only a public JWK, never the private key.
195
241
 
196
242
  `--app-id` takes `app:<client id>` or the bare client id. The leaves:
197
243
 
@@ -205,24 +251,50 @@ For example, ask the agent: "Run `crtr cloud apps register --name Texty --origin
205
251
  | `crtr cloud apps asks --app-id <id>` | Lists what an app may ask for, with whether it asks now. Large sources such as `integrations` print as a count. Narrow it with `--query sms:send`, `--source sms`, `--group providers\|apps\|agents\|space`, or `--query <text>`; for example `crtr cloud apps asks --app-id <id> --source sms`. |
206
252
  | `crtr cloud apps set-scopes --app-id <id> --scope …` | Replaces the scope list. Repeat `--scope` for every scope; `--clear` empties it; `crtr:act` needs `crtr:llm`. |
207
253
  | `crtr cloud apps set-requires --app-id <id> --provider …` | Names providers your app does not work without (for example `context`); `--clear` empties it. |
208
- | `crtr cloud apps keys add --app-id <id> --jwk '<public JWK>'` | Adds a signing key beside any existing one. Answers `created: false` if that key is already on the app. |
209
- | `crtr cloud apps set-listing --app-id <id> --login-start-url … --text-welcome …` (also `--listed`, `--price`) | Sets the listing fields; pass at least one. A loopback `--login-start-url` is accepted. `--listed true` was not run during this guide's checks. |
210
- | `crtr cloud apps testers add --app-id <id> --email …` (or `--phone …`) | Lets that person sign in while the app is in testing; at most 100. |
254
+ | `crtr cloud apps keys add --app-id <id> --jwk '<public JWK>'` | Adds a signing key beside any existing one. Answers `created: false` if that key is already on the app. `keygen --app` calls it for you. |
255
+ | `crtr cloud apps keys list --app-id <id>` | Lists the keys with their `kid` and `active` or `retired` status. |
256
+ | `crtr cloud apps keys retire --app-id <id> --kid <kid>` | Retires a key so it no longer signs in. |
257
+ | `crtr cloud apps set-listing --app-id <id> --login-start-url … --text-welcome …` (also `--listed`, `--price`) | Sets the listing fields; pass at least one. A loopback `--login-start-url` is accepted. `--listed true --price free` lists the app, so `cloud apps install` finds it; while the app is in testing it still stays out of the marketplace list. |
258
+ | `crtr cloud apps install --app-id <id>` | Texts an install link to the signed-in person. If they already hold a grant it answers `status: already_have` and the install URL instead of texting. The app must be listed. |
259
+ | `crtr cloud apps testers add --app-id <id> --email …` (or `--phone …`) | Lets that person sign in while the app is in testing; at most 100. Not yet run on production. |
211
260
  | `crtr cloud apps testers list --app-id <id>` | Lists the testers. |
212
- | `crtr cloud apps testers remove --app-id <id> --email …` (or `--phone …`) | Removes a tester and revokes their grants for the app. |
261
+ | `crtr cloud apps testers remove --app-id <id> --email …` (or `--phone …`) | Removes a tester and revokes their grants for the app. Not yet run on production. |
213
262
  | `crtr cloud apps delete --app-id <id> --confirm <client id>` | Deletes the app and removes every person's grant for it. Only you at a joined machine's terminal can run it: an agent, including your chat agent, is refused with `provider_refused`. |
214
263
 
215
264
  ### Deleting an app
216
265
 
217
- The console has no control for deleting an app. `crtr cloud apps delete` is the way, and it runs only from a joined machine's terminal, which a laptop cannot be yet. Until then, email `support@northlight.ai` with the client id: Northlight staff can delete an app with an operator command. Deleting removes every person's grant for the app, so each runtime deletes the app's runs, space and schedules, and the app's next refresh for them fails with `invalid_grant`. It cannot be undone, and apps that hold the provider role cannot be deleted. Reusing an app for tests is usually simpler: change its redirect URIs and keys instead.
266
+ ```bash
267
+ crtr cloud apps delete --app-id <client id> --confirm <client id>
268
+ ```
269
+
270
+ It answers `deleted: true` and `removed_grants`, the number of people's grants it removed. Each runtime deletes the app's runs, space and schedules, and the app's next refresh fails with `invalid_grant`. The app's key stops working, the app's page answers 404 and `mine` no longer lists it. It cannot be undone, and apps that hold the provider role cannot be deleted.
271
+
272
+ Only you can delete: run it yourself in the terminal of your joined machine. An agent, including your chat agent, is refused with `provider_refused`: "Only you can delete an app. Ask the person to run `crtr cloud apps delete` in their terminal." Reusing an app for tests is often simpler: change its redirect URIs and keys instead.
218
273
 
219
274
  ### Let people find and install it
220
275
 
221
- `login_start_url` is where Northlight starts sign-in for a person who installs your app from a link. In the console, open **Profile**, and under **Marketplace** set **Sign-in start URL**, **Text welcome** (one or two plain sentences, at most 120 characters, no links) and **Listed in the marketplace**. Your `/auth/login` route from step 7 is a valid start URL. Agents can install an app for people by text only while it is listed.
276
+ `login_start_url` is where Northlight starts sign-in for a person who installs your app from a texted link (specified behavior: pressing the texted link was not run on production, only the text send was). Your `/auth/login` route from step 7 is a valid start URL, and a loopback one is accepted:
277
+
278
+ ```bash
279
+ crtr cloud apps set-listing --app-id <client id> --listed true --price free \
280
+ --login-start-url http://localhost:3000/auth/login \
281
+ --text-welcome "Texts you a summary of your day."
282
+ ```
283
+
284
+ `--text-welcome` is one or two plain sentences, at most 120 characters, with no links. Agents can install an app for people by text only while it is listed (`--listed true`). `crtr cloud apps install --app-id <client id>` sends the signed-in person a text with the install link. Run it only for yourself or for a person who asked: it sends a real text.
222
285
 
223
286
  ### Rotate keys
224
287
 
225
- Add a new key beside the old one, switch, verify with a real sign-in, then retire the old key. Use `keygen --out local/app-key-next.json` without `--app`, paste its public JWK with **Add key** on the **Sign-in** tab, and retire the old one there. [Rotating your key](../getting-started.md#rotating-your-key) has the full sequence.
288
+ Add a new key beside the old one, switch, verify with a real sign-in, then retire the old key:
289
+
290
+ ```bash
291
+ npx @north-light/sdk keygen --out local/app-key.next.json --app <client id> --yes
292
+ # point CROUTER_KEY_FILE at local/app-key.next.json, restart, and sign in once
293
+ crtr cloud apps keys list --app-id <client id>
294
+ crtr cloud apps keys retire --app-id <client id> --kid <old kid>
295
+ ```
296
+
297
+ The old key keeps working until you retire it, and your app's refreshes keep working after. [Rotating your key](../getting-started.md#rotating-your-key) has the full sequence.
226
298
 
227
299
  ## Getting a result back from a run
228
300
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.425",
3
+ "version": "0.3.426",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -137,7 +137,7 @@
137
137
  },
138
138
  "license": "UNLICENSED",
139
139
  "dependencies": {
140
- "@north-light/identity": "0.3.425",
140
+ "@north-light/identity": "0.3.426",
141
141
  "@earendil-works/pi-agent-core": "1.1.0",
142
142
  "@earendil-works/pi-ai": "1.1.0",
143
143
  "@earendil-works/pi-coding-agent": "1.1.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.425",
3
+ "version": "0.3.426",
4
4
  "description": "crouter cloud access-token verification and scope rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.425",
3
+ "version": "0.3.426",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/cli",
9
- "version": "0.3.425",
9
+ "version": "0.3.426",
10
10
  "hasInstallScript": true,
11
11
  "license": "UNLICENSED",
12
12
  "workspaces": [
@@ -18,7 +18,7 @@
18
18
  "@earendil-works/pi-ai": "1.1.0",
19
19
  "@earendil-works/pi-coding-agent": "1.1.0",
20
20
  "@earendil-works/pi-tui": "1.1.0",
21
- "@north-light/identity": "0.3.425",
21
+ "@north-light/identity": "0.3.426",
22
22
  "cron-parser": "^5.6.0",
23
23
  "esbuild": "^0.27.7",
24
24
  "jose": "^6.2.12",
@@ -12095,17 +12095,17 @@
12095
12095
  },
12096
12096
  "packages/crouter-api": {
12097
12097
  "name": "@north-light/api",
12098
- "version": "0.3.425",
12098
+ "version": "0.3.426",
12099
12099
  "license": "UNLICENSED"
12100
12100
  },
12101
12101
  "packages/crouter-env-docker": {
12102
12102
  "name": "@north-light/env-docker",
12103
- "version": "0.3.425",
12103
+ "version": "0.3.426",
12104
12104
  "license": "UNLICENSED"
12105
12105
  },
12106
12106
  "packages/crouter-identity": {
12107
12107
  "name": "@north-light/identity",
12108
- "version": "0.3.425",
12108
+ "version": "0.3.426",
12109
12109
  "license": "UNLICENSED",
12110
12110
  "dependencies": {
12111
12111
  "jose": "^6.2.1"
@@ -12113,20 +12113,20 @@
12113
12113
  },
12114
12114
  "packages/crouter-plugin": {
12115
12115
  "name": "@north-light/plugin",
12116
- "version": "0.3.425",
12116
+ "version": "0.3.426",
12117
12117
  "license": "UNLICENSED",
12118
12118
  "dependencies": {
12119
- "@north-light/api": "0.3.425",
12120
- "@north-light/identity": "0.3.425"
12119
+ "@north-light/api": "0.3.426",
12120
+ "@north-light/identity": "0.3.426"
12121
12121
  }
12122
12122
  },
12123
12123
  "packages/crouter-sdk": {
12124
12124
  "name": "@north-light/sdk",
12125
- "version": "0.3.425",
12125
+ "version": "0.3.426",
12126
12126
  "license": "UNLICENSED",
12127
12127
  "dependencies": {
12128
- "@north-light/api": "0.3.425",
12129
- "@north-light/identity": "0.3.425",
12128
+ "@north-light/api": "0.3.426",
12129
+ "@north-light/identity": "0.3.426",
12130
12130
  "jose": "^6.2.1"
12131
12131
  },
12132
12132
  "bin": {