@catheadowl/ukp 0.2.6 → 0.3.0
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/README.md +83 -16
- package/SECURITY.md +69 -0
- package/bin/ukp.js +31 -7
- package/docs/remote-deployment.md +86 -8
- package/package.json +11 -6
- package/skills/ukp/SKILL.md +32 -0
- package/src/capabilities/nav.ts +209 -444
- package/src/capabilities/qmd.ts +19 -0
- package/src/capabilities/read.ts +18 -2
- package/src/capabilities/remote-client.ts +4 -2
- package/src/capabilities/search.ts +23 -7
- package/src/capabilities/session.ts +455 -0
- package/src/capabilities/tls-identity.ts +54 -3
- package/src/cli.ts +32 -64
- package/src/commands/guide-content/client.txt +19 -6
- package/src/commands/guide-content/remote.txt +21 -0
- package/src/commands/guide-content/rg.txt +2 -2
- package/src/commands/init.ts +224 -9
- package/src/commands/inventory.ts +277 -17
- package/src/commands/nav.ts +17 -12
- package/src/commands/read.ts +1 -1
- package/src/commands/rg.ts +2 -2
- package/src/commands/search.ts +19 -3
- package/src/commands/serve-task.ts +5 -5
- package/src/commands/serve.ts +313 -29
- package/src/commands/session.ts +541 -0
- package/src/commands/use.ts +178 -0
- package/src/config/agent-backed.ts +136 -0
- package/src/config/manifest.ts +39 -3
- package/src/manifest.yml +15 -0
- package/src/path-safety.ts +9 -4
- package/src/plumbing/token-file.ts +368 -0
- package/src/registry.ts +78 -1
- package/src/server.ts +228 -45
- package/src/spawn-relay.ts +21 -3
package/README.md
CHANGED
|
@@ -12,6 +12,10 @@ memorize, no provider-specific commands to relearn.
|
|
|
12
12
|
> [!NOTE]
|
|
13
13
|
> Early-stage CLI (MVP) — commands and behavior may still change.
|
|
14
14
|
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @catheadowl/ukp # or: bun add -g @catheadowl/ukp
|
|
17
|
+
```
|
|
18
|
+
|
|
15
19
|
```bash
|
|
16
20
|
$ ukp init service --name notes && ukp register
|
|
17
21
|
initialized Service: notes
|
|
@@ -27,11 +31,15 @@ $ ukp read ukp://notes/2026/q3-plan.md # read straight from the reference
|
|
|
27
31
|
# Q3 Plan
|
|
28
32
|
Goals for Q3: ship the knowledge plane.
|
|
29
33
|
|
|
30
|
-
$ echo '[capabilities.propose]' >> .ukp/service.toml # the write
|
|
34
|
+
$ echo '[capabilities.propose]' >> .ukp/service.toml # the write path is opt-in: declare propose
|
|
35
|
+
$ echo '# typo fix' > typo-fix.md # a proposal is any Markdown draft
|
|
31
36
|
$ ukp propose --endpoint notes --id typo-fix --file typo-fix.md
|
|
32
37
|
proposal typo-fix created (revision 1)
|
|
33
38
|
```
|
|
34
39
|
|
|
40
|
+
A `ukp://` reference is not a web link — it names a registered endpoint
|
|
41
|
+
(local or remote); open one with `ukp read <uri>`.
|
|
42
|
+
|
|
35
43
|
## Why UKP
|
|
36
44
|
|
|
37
45
|
Knowledge work starts in folders, but repeated use needs stable names,
|
|
@@ -50,8 +58,9 @@ predictable scope, and a single command surface. Use UKP when:
|
|
|
50
58
|
|
|
51
59
|
- Register local folders as named knowledge endpoints — a registered folder
|
|
52
60
|
is a *Service*, addressed by its endpoint name.
|
|
53
|
-
- `ukp inspect` shows which endpoints the current scope selects
|
|
54
|
-
|
|
61
|
+
- `ukp inspect` shows which endpoints the current scope selects (the
|
|
62
|
+
nearest `.ukp/client.toml`; every registered endpoint when there is
|
|
63
|
+
none) — see what a command will touch before running it.
|
|
55
64
|
- Navigate the Markdown structure of an endpoint — folders, descriptions,
|
|
56
65
|
depth — with zero provider dependency.
|
|
57
66
|
- Read endpoint files through the built-in `read/file` capability — if a
|
|
@@ -78,7 +87,8 @@ predictable scope, and a single command surface. Use UKP when:
|
|
|
78
87
|
`[capabilities]` table still registers one (derived `read`/`nav` only).
|
|
79
88
|
- Give agents JSON output (`--json`; `ukp read` uses `--format json`),
|
|
80
89
|
reference sidecars, docid handoff keys, and durable `ukp://` references
|
|
81
|
-
when they need machine-readable results
|
|
90
|
+
when they need machine-readable results; every `ukp://` reference opens
|
|
91
|
+
with `ukp read <uri>`.
|
|
82
92
|
|
|
83
93
|
UKP owns endpoint names, Registry bindings, scope, and output shape; the
|
|
84
94
|
provider owns collection setup, indexes, ranking, and maintenance. UKP
|
|
@@ -126,14 +136,17 @@ endpoint without it; `propose` works wherever it's declared.
|
|
|
126
136
|
|
|
127
137
|
An agent can carry out the same flow on your behalf.
|
|
128
138
|
|
|
129
|
-
For workspace defaults, use `ukp guide client`
|
|
139
|
+
For workspace defaults, run `ukp use <endpoint>` (see `ukp guide client`) —
|
|
140
|
+
it writes `.ukp/client.toml` and keeps it committable in one step.
|
|
130
141
|
|
|
131
142
|
## Commands
|
|
132
143
|
|
|
133
|
-
Grouping below follows `ukp --help`;
|
|
134
|
-
`ukp <verb
|
|
135
|
-
(`ukp
|
|
136
|
-
|
|
144
|
+
Grouping below follows `ukp --help`; primary capability commands stay a flat
|
|
145
|
+
`ukp <verb>`, while multi-verb capability families appear as object command
|
|
146
|
+
groups (`ukp init service`, `ukp guide <topic>`). `--endpoint <name>`
|
|
147
|
+
is the canonical endpoint selector (`ukp unregister <name>` still takes the
|
|
148
|
+
endpoint as a positional legacy form); `-g` selects the full local Host
|
|
149
|
+
Registry where supported.
|
|
137
150
|
|
|
138
151
|
The same five commands — search, read, nav, rg, propose — work on local and
|
|
139
152
|
remote endpoints; consuming a remote endpoint is a one-command gesture
|
|
@@ -143,7 +156,7 @@ Endpoint commands:
|
|
|
143
156
|
|
|
144
157
|
| Command | What it does |
|
|
145
158
|
|---|---|
|
|
146
|
-
| `ukp search` | Indexed search through the endpoint's provider (QMD today); `--recursive` expands the manifest's declared `dependencies`. Results hand off
|
|
159
|
+
| `ukp search` | Indexed search through the endpoint's provider (QMD today); `--recursive` expands the manifest's declared `dependencies`. Results hand off a docid handoff key and verified `ukp://` references. |
|
|
147
160
|
| `ukp read` | Reads an endpoint-scoped resource — exact path, `ukp://` URI, or a `docid` handed off by search results. |
|
|
148
161
|
| `ukp nav` | Markdown outline of an endpoint (`--depth`, respects `.gitignore`); on by default, configurable via `[capabilities.nav]`. |
|
|
149
162
|
| `ukp rg` | Lexical grep (ripgrep) across endpoint files — provider-free, on by default; `--files` enumerates them instead of searching. Results become `read`-ready `ukp://` references. A missing rg binary skips the endpoint with a warning. |
|
|
@@ -153,9 +166,14 @@ Registry commands:
|
|
|
153
166
|
|
|
154
167
|
| Command | What it does |
|
|
155
168
|
|---|---|
|
|
156
|
-
| `ukp init service` | Creates a minimal `.ukp/service.toml`. |
|
|
157
|
-
| `ukp
|
|
158
|
-
| `ukp
|
|
169
|
+
| `ukp init service` | Creates a minimal `.ukp/service.toml`. The `.ukp/.gitignore` it drops ignores machine-local state only (the instance identity; the serve TLS identity lives in `~/.ukp/tls/`) — `service.toml` and `client.toml` are pure declarations and committable by design (a fresh clone re-registers in one `ukp register`). |
|
|
170
|
+
| `ukp use <endpoint> [endpoint...]` | One-step consumer gesture: sets this workspace's `default_endpoints` (`.ukp/client.toml`, explicit replace, idempotent — every name must be registered) and keeps the config version-visible in the same step. `ukp inspect` confirms the active scope. |
|
|
171
|
+
| `ukp register` / `ukp unregister --endpoint <name>` | Manages Host Registry bindings — local folders and remote endpoints alike (`ukp register --url`, see Remote in 60 seconds). `--name <handle>` disambiguates collisions: `notes` vs `ali-notes`. A folder that moved re-binds in one step: `ukp register --rebind` from the new location (local bindings). |
|
|
172
|
+
| `ukp list` | Lists endpoint bindings and capabilities under the **workspace scope** by default (nearest `.ukp/client.toml`; every registered endpoint when there is none); `-g` lists the full Host Registry without reading the client config. `--json` emits the offline machine snapshot (`ukp.list.v1`) riding the same scope — stable field names, the same data core as the text listing, zero network (remote bindings are registry facts only, so their capability fields are `null`); pin `-g` for the full snapshot. New endpoints on a host door show as a stderr note (`door <origin>: N unimported endpoint(s) …`) — importing stays an explicit gesture. |
|
|
173
|
+
|
|
174
|
+
A legacy `.ukp/.gitignore` (the retired `*` self-ignore template) migrates
|
|
175
|
+
to this posture in passing whenever `ukp register` (local folders) or
|
|
176
|
+
`ukp use` meets one.
|
|
159
177
|
|
|
160
178
|
Operations commands:
|
|
161
179
|
|
|
@@ -181,9 +199,11 @@ Help commands:
|
|
|
181
199
|
## Remote in 60 seconds
|
|
182
200
|
|
|
183
201
|
`ukp serve` exposes an endpoint over HTTP. Authentication is deny-by-default:
|
|
184
|
-
serving with a `--host` beyond loopback requires
|
|
185
|
-
|
|
186
|
-
|
|
202
|
+
serving with a `--host` beyond loopback requires a credential — the
|
|
203
|
+
`UKP_SERVE_TOKEN` env token (full power) or a labeled key issued into
|
|
204
|
+
the `~/.ukp/tokens` file (tokenless serving needs an explicit
|
|
205
|
+
`--allow-anonymous` and is refused off loopback). The two fastest
|
|
206
|
+
paths — each block notes where its commands run:
|
|
187
207
|
|
|
188
208
|
```bash
|
|
189
209
|
# From the consumer machine — SSH between two personal machines; the door is
|
|
@@ -199,6 +219,21 @@ UKP_SERVE_TOKEN=<token> ukp serve --endpoint <name> --host 0.0.0.0 --port 8570 -
|
|
|
199
219
|
ukp register --url https://<ip>:8570 --endpoint <name> --token <token>
|
|
200
220
|
```
|
|
201
221
|
|
|
222
|
+
Handing a second machine or an agent a narrower key? Issue labeled, scoped,
|
|
223
|
+
revocable keys instead of sharing the env token (ON THE HOST):
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
ukp serve token add liku "notes:ro,cad:rw" # prints the raw key ONCE
|
|
227
|
+
ukp serve token list # labels, key ids, scopes
|
|
228
|
+
ukp serve token revoke liku # next request is a 401
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Scopes are `name:ro|rw` comma-separated (full power is the explicit `*:rw`);
|
|
232
|
+
the file stores hashes only, and the door re-reads it on every request —
|
|
233
|
+
deleting a line revokes without a restart. A scoped key sees only its own
|
|
234
|
+
endpoints: out-of-scope names answer exactly like nonexistent ones. While
|
|
235
|
+
`UKP_SERVE_TOKEN` is set it takes precedence and the file is not consulted.
|
|
236
|
+
|
|
202
237
|
NAT/EIP cloud host (public IP on no NIC)? Name it with
|
|
203
238
|
`ukp serve --tls --tls-san <public-ip>` — the persisted certificate re-signs
|
|
204
239
|
over the same key, so the pin (and every registration) survives; details in
|
|
@@ -219,6 +254,36 @@ registry), Caddy and Tailscale fronting, the certificate pinning lifecycle,
|
|
|
219
254
|
and the hardening posture — lives in the
|
|
220
255
|
[deployment guide](https://github.com/CatheadOwl/ukp/blob/main/docs/remote-deployment.md).
|
|
221
256
|
|
|
257
|
+
## Security
|
|
258
|
+
|
|
259
|
+
UKP is local-first: nothing listens on the network until you run
|
|
260
|
+
`ukp serve`. When you do, the surface is deny-by-default — serving beyond
|
|
261
|
+
loopback without a credential is refused, and anonymous serving is an
|
|
262
|
+
explicit flag that only works on loopback. Credentials come in two forms:
|
|
263
|
+
the `UKP_SERVE_TOKEN` env token (full power, takes precedence while set)
|
|
264
|
+
and labeled keys in the 0600 `~/.ukp/tokens` file (hash-only storage,
|
|
265
|
+
required per-key scopes, revocation by deleting the line — effective on
|
|
266
|
+
the next request). Transport is `https://`, pinned
|
|
267
|
+
automatically at registration (a certificate that renews on the same key
|
|
268
|
+
re-pins silently; a different key hard-blocks with both fingerprints), or
|
|
269
|
+
`ssh://`, where SSH carries both encryption and authentication. A
|
|
270
|
+
`--select` door neither routes, declares, nor lists unselected endpoints —
|
|
271
|
+
excluded and unknown names return byte-identical 404s, and a scoped key's
|
|
272
|
+
out-of-scope endpoints answer the same way.
|
|
273
|
+
|
|
274
|
+
Accepted tradeoffs, disclosed rather than hidden: tokens are long-lived
|
|
275
|
+
static bearer secrets stored client-side in the 0600 registry file or an
|
|
276
|
+
environment variable (the same tradeoff as `~/.aws/credentials`); the
|
|
277
|
+
Windows door generator (`ukp serve --print-task`) embeds the token in a
|
|
278
|
+
0600 `start-door.cmd` it never prints; identity pinning is
|
|
279
|
+
trust-on-first-use (an SSH-style first-connect window). Hardening
|
|
280
|
+
guidance — token strength, reverse proxies and their same-machine
|
|
281
|
+
blind spot, firewalls — is in the
|
|
282
|
+
[deployment guide](https://github.com/CatheadOwl/ukp/blob/main/docs/remote-deployment.md).
|
|
283
|
+
|
|
284
|
+
Found something? Report it privately — see [SECURITY.md](SECURITY.md);
|
|
285
|
+
please don't use public issues for security reports.
|
|
286
|
+
|
|
222
287
|
## Not Yet
|
|
223
288
|
|
|
224
289
|
- remote operation of `update`, and a formally specified network protocol
|
|
@@ -231,6 +296,8 @@ and the hardening posture — lives in the
|
|
|
231
296
|
## Documentation
|
|
232
297
|
|
|
233
298
|
- [Remote deployment](https://github.com/CatheadOwl/ukp/blob/main/docs/remote-deployment.md) — serving endpoints across machines: the full guide.
|
|
299
|
+
Also ships inside the npm package (`docs/remote-deployment.md` in the
|
|
300
|
+
installed package); the other docs live in the GitHub repository.
|
|
234
301
|
- [Contributing](https://github.com/CatheadOwl/ukp/blob/main/CONTRIBUTING.md) — development setup and release packaging.
|
|
235
302
|
- [Changelog](https://github.com/CatheadOwl/ukp/blob/main/CHANGELOG.md) — what changed per release.
|
|
236
303
|
- Repository: https://github.com/CatheadOwl/ukp ·
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a vulnerability
|
|
4
|
+
|
|
5
|
+
Report privately using GitHub security advisories:
|
|
6
|
+
|
|
7
|
+
https://github.com/CatheadOwl/ukp/security/advisories/new
|
|
8
|
+
|
|
9
|
+
Please do not open public issues for security reports. We follow
|
|
10
|
+
coordinated vulnerability disclosure (the CERT process, with the 90-day
|
|
11
|
+
convention as the default clock) and credit reporters unless they prefer
|
|
12
|
+
to stay anonymous. Good-faith research on your own deployments is welcome.
|
|
13
|
+
|
|
14
|
+
## Supported versions
|
|
15
|
+
|
|
16
|
+
Only the latest published release line receives security fixes.
|
|
17
|
+
|
|
18
|
+
## Scope
|
|
19
|
+
|
|
20
|
+
In scope:
|
|
21
|
+
|
|
22
|
+
- the `ukp` CLI runtime: local capability commands, `ukp serve`
|
|
23
|
+
(transport, authentication, TLS identity, door routing), the generated
|
|
24
|
+
door artifacts (`ukp serve --print-task`), and credential handling on
|
|
25
|
+
consumer machines;
|
|
26
|
+
- the published package supply chain (npm tarball, GitHub repository).
|
|
27
|
+
|
|
28
|
+
Out of scope:
|
|
29
|
+
|
|
30
|
+
- hardening of your own host, SSH daemon, or reverse proxy — deployment
|
|
31
|
+
guidance exists, but operator-side misconfiguration is not a UKP
|
|
32
|
+
vulnerability;
|
|
33
|
+
- private working materials that are not part of the published package.
|
|
34
|
+
|
|
35
|
+
## Posture summary
|
|
36
|
+
|
|
37
|
+
- Local-first: no network listener exists until you run `ukp serve`.
|
|
38
|
+
- Deny-by-default serving: serving beyond loopback without
|
|
39
|
+
`UKP_SERVE_TOKEN` is refused; anonymous serving is an explicit flag and
|
|
40
|
+
loopback-only.
|
|
41
|
+
- Transport: `https://` with registration-time pinning (self-signed
|
|
42
|
+
certificates are pinned by SPKI; a renewal on the same key re-pins
|
|
43
|
+
transparently, a different key hard-blocks) or `ssh://`, where SSH
|
|
44
|
+
carries both encryption and authentication.
|
|
45
|
+
- Door scoping: a `--select` door neither routes, declares, nor lists
|
|
46
|
+
unselected endpoints; excluded and unknown names return byte-identical
|
|
47
|
+
404s.
|
|
48
|
+
|
|
49
|
+
Deployment hardening (token strength, reverse proxies, firewalls,
|
|
50
|
+
systemd) is covered in the
|
|
51
|
+
[deployment guide](https://github.com/CatheadOwl/ukp/blob/main/docs/remote-deployment.md).
|
|
52
|
+
|
|
53
|
+
## Known, accepted tradeoffs
|
|
54
|
+
|
|
55
|
+
Disclosed rather than hidden; each has a documented trigger for change:
|
|
56
|
+
|
|
57
|
+
- Tokens are long-lived static bearer secrets. OAuth flows are deferred
|
|
58
|
+
until a multi-tenant or hosted scenario exists.
|
|
59
|
+
- Read and write use the same token today; separation becomes mandatory
|
|
60
|
+
with the first remote content-write capability.
|
|
61
|
+
- Client-side tokens live in the 0600 registry file or an environment
|
|
62
|
+
variable, in plaintext — the same tradeoff as `~/.aws/credentials`.
|
|
63
|
+
- The Windows door artifact embeds the token in a 0600 `start-door.cmd`
|
|
64
|
+
and never prints it; a file that ever left the machine burns the token.
|
|
65
|
+
- Identity pinning is trust-on-first-use, with the SSH-style
|
|
66
|
+
first-connect window that implies.
|
|
67
|
+
- npm provenance attestations prove where and how a package was built,
|
|
68
|
+
not that the code is safe — verify with `npm audit signatures` and
|
|
69
|
+
treat provenance as one signal among several.
|
package/bin/ukp.js
CHANGED
|
@@ -56,28 +56,52 @@ function bunCandidates() {
|
|
|
56
56
|
return candidates;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function fail(detail) {
|
|
59
|
+
function fail(detail, hint = "") {
|
|
60
60
|
process.stderr.write(
|
|
61
61
|
"ukp: no bun executable found (" + detail + ").\n"
|
|
62
62
|
+ "ukp runs on bun — install it (https://bun.sh), or point UKP_BUN at the\n"
|
|
63
63
|
+ "bun executable. Standard install locations (~/.bun/bin and the npm\n"
|
|
64
|
-
+ "global prefix) are checked before PATH.\n"
|
|
64
|
+
+ "global prefix) are checked before PATH.\n"
|
|
65
|
+
+ hint,
|
|
65
66
|
);
|
|
66
67
|
process.exit(1);
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
/** Windows-only diagnosis for a UKP_BUN that the user's shell accepts but
|
|
71
|
+
* existsSync rejects: name the shape mismatch instead of implying the file
|
|
72
|
+
* is absent. Two real shapes (0.3.0 release review): Git Bash converts an
|
|
73
|
+
* exported MSYS path (/c/.../bun) to a native form WITHOUT the .exe
|
|
74
|
+
* extension before this launcher sees it, so the .exe sibling exists while
|
|
75
|
+
* the named path does not; and a variable set outside a converting shell
|
|
76
|
+
* (cmd/PowerShell) reaches us as the raw /c/... or /cygdrive/... form,
|
|
77
|
+
* which Windows file APIs cannot resolve at all. */
|
|
78
|
+
function ukpBunHint() {
|
|
79
|
+
const value = process.env.UKP_BUN;
|
|
80
|
+
if (!isWindows || typeof value !== "string") return "";
|
|
81
|
+
if (/^\/(?:[a-z]|cygdrive\/[a-z])\//i.test(value)) {
|
|
82
|
+
return "ukp: UKP_BUN looks like an MSYS-style path; Windows needs a native "
|
|
83
|
+
+ "form like C:/path/to/bun.exe\n";
|
|
84
|
+
}
|
|
85
|
+
if (!/\.exe$/i.test(value) && existsSync(value + ".exe")) {
|
|
86
|
+
return "ukp: UKP_BUN omits the .exe extension (the executable sits next to "
|
|
87
|
+
+ "it); use its full name, e.g. C:/path/to/bun.exe\n";
|
|
88
|
+
}
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
|
|
69
92
|
const bunPath = bunCandidates().find((candidate) => existsSync(candidate));
|
|
70
93
|
if (bunPath === undefined) {
|
|
71
|
-
fail("looked at UKP_BUN, the standard install locations, and PATH");
|
|
94
|
+
fail("looked at UKP_BUN, the standard install locations, and PATH", ukpBunHint());
|
|
72
95
|
}
|
|
73
96
|
|
|
74
97
|
// In-process handoff when this launcher already runs under bun (bun's own
|
|
75
98
|
// bin links and `bunx`): importing the CLI keeps it in THIS process, where
|
|
76
|
-
// stdout/stderr keep their real TTY identity (
|
|
99
|
+
// stdout/stderr keep their real TTY identity (isTTY) — a spawnSync
|
|
77
100
|
// grandchild re-inherits duplicated handles, and on some hosts that layer
|
|
78
|
-
// degraded TTY detection (the third dogfood round: progress
|
|
79
|
-
// skipped while a direct run on the same console had
|
|
80
|
-
// also starts faster. The node-interpreted path (npm shims) keeps
|
|
101
|
+
// degraded TTY detection (the third dogfood round: progress frames
|
|
102
|
+
// silently skipped while a direct run on the same console had them). One
|
|
103
|
+
// process also starts faster. The node-interpreted path (npm shims) keeps
|
|
104
|
+
// spawning.
|
|
81
105
|
if (runningUnderBun) {
|
|
82
106
|
const { runCli } = await import(pathToFileURL(cli).href);
|
|
83
107
|
const exit = runCli(process.argv.slice(2));
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Operational recipes for serving UKP endpoints remotely — ssh wake, native TLS, Windows resident door, socket activation, fronting proxies, scoped keys, and hardening posture
|
|
3
|
+
---
|
|
4
|
+
|
|
1
5
|
# Remote deployment
|
|
2
6
|
|
|
3
7
|
Operational guide for exposing UKP endpoints over the network. For the
|
|
@@ -28,6 +32,17 @@ the same host forwards from the public side to the loopback bind, so proxied
|
|
|
28
32
|
deployments treat the token as mandatory (serve cannot see past its own bind
|
|
29
33
|
address).
|
|
30
34
|
|
|
35
|
+
The complete route list (ukp-remote v1) — what a client or a fronting proxy's
|
|
36
|
+
path allowlist must account for: `GET /.well-known/ukp.json` (the public
|
|
37
|
+
discovery document — the door's roster document in host door mode, where each
|
|
38
|
+
endpoint also serves its own at `/e/<name>/.well-known/ukp.json`), the read
|
|
39
|
+
face — `POST /v1/search`, `GET /v1/read`, `GET /v1/nav`, `GET /v1/rg` (each
|
|
40
|
+
prefixed `/e/<name>` at a door) — and the one write route, `PUT
|
|
41
|
+
/v1/propose/<id>` (`PUT /e/<name>/v1/propose/<id>` at a door): the proposal
|
|
42
|
+
upsert. Propose needs the endpoint to declare the capability, and a scoped
|
|
43
|
+
key must carry that endpoint at `rw` (`ro` keys reach the read face only —
|
|
44
|
+
see Scoped keys below).
|
|
45
|
+
|
|
31
46
|
## Via SSH (two personal machines — zero extra components, zero ceremony)
|
|
32
47
|
|
|
33
48
|
The `ssh://` URL scheme makes the transport transparent AND wakes the door
|
|
@@ -80,9 +95,12 @@ supported (its `-c` quoting drops the wake form).
|
|
|
80
95
|
|
|
81
96
|
## Native TLS (bare IP, no domain — zero extra components)
|
|
82
97
|
|
|
83
|
-
`--tls` self-signs on first start (identity = keypair, persisted
|
|
84
|
-
|
|
85
|
-
|
|
98
|
+
`--tls` self-signs on first start (identity = keypair, persisted in the
|
|
99
|
+
machine home beside the host registry — `~/.ukp/tls/<endpoint-name>/` for a
|
|
100
|
+
single endpoint, `~/.ukp/tls/` for a host door; SAN covers the host's
|
|
101
|
+
addresses, so the identity never travels with the knowledge folder and a
|
|
102
|
+
re-cloned endpoint on the same host keeps its pin). Clients pin the
|
|
103
|
+
certificate automatically at registration — same command as any other
|
|
86
104
|
remote, no flags added:
|
|
87
105
|
|
|
88
106
|
```bash
|
|
@@ -115,6 +133,57 @@ only grows: the re-sign keeps everything the certificate already carried
|
|
|
115
133
|
narrower requests reuse the certificate untouched. Explicit `--tls-cert` certificates carry their own SAN, so
|
|
116
134
|
`--tls-san` next to them is a usage error, not a no-op.
|
|
117
135
|
|
|
136
|
+
## Scoped keys (second machines, agents — labeled, revocable)
|
|
137
|
+
|
|
138
|
+
`UKP_SERVE_TOKEN` is the full-power shortcut: any listed token reaches every
|
|
139
|
+
endpoint and the write face. When you hand access to a second machine or an
|
|
140
|
+
agent, issue a **labeled, scoped key** instead — the issuance face is a serve
|
|
141
|
+
subcommand, and the credential store is a plain file you can audit:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# ON THE HOST (run without UKP_SERVE_TOKEN set — while the env token is
|
|
145
|
+
# present it takes precedence and the file is not consulted at all):
|
|
146
|
+
ukp serve token add liku "notes:ro,cad:rw"
|
|
147
|
+
# issued key for 'liku' (scopes: notes:ro,cad:rw)
|
|
148
|
+
# ukp_liku_3f9c...TQ <- the raw key, shown ONCE, never stored
|
|
149
|
+
# stored in ~/.ukp/tokens (0600, hash-only)...
|
|
150
|
+
|
|
151
|
+
# serve the door with NO env token — the key file arms the gate:
|
|
152
|
+
ukp serve --host 0.0.0.0 --tls
|
|
153
|
+
|
|
154
|
+
# consumer machine registers with the raw key as the token:
|
|
155
|
+
ukp register --url https://<ip>:8570 --token ukp_liku_...
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
How the pieces behave:
|
|
159
|
+
|
|
160
|
+
- **Scopes** are `name:ro|rw`, comma-separated (Docker-registry style); full
|
|
161
|
+
power is the explicit `*:rw` — a key with no scopes is a usage error, not a
|
|
162
|
+
wildcard. `rw` adds the propose route; `ro` keys get read/search/nav/rg
|
|
163
|
+
only. An endpoint outside the key's scope answers exactly like a name that
|
|
164
|
+
does not exist (same 404, same roster) — the key cannot even learn what it
|
|
165
|
+
cannot see.
|
|
166
|
+
- **The file** is `~/.ukp/tokens`, one line per key (`label key-id sha256$
|
|
167
|
+
hash scopes`), 0600, hash-only — a leaked file is not a leaked credential.
|
|
168
|
+
On POSIX, group/other permissions make the file unusable (machine refusal,
|
|
169
|
+
OpenSSH-style); Windows relies on the %USERPROFILE% ACLs instead.
|
|
170
|
+
- **Revocation is deleting the line.** `ukp serve token revoke <label>`
|
|
171
|
+
removes every key carrying that label; the running door re-reads the file
|
|
172
|
+
on every request, so the next call is a 401 — no restart, no deploy step.
|
|
173
|
+
Hand-editing (deleting or `#`-commenting a line) works too.
|
|
174
|
+
- **Rotation** is zero-downtime: `token add` with an existing label keeps the
|
|
175
|
+
old key active; hand out the new one, then revoke the label when the old
|
|
176
|
+
key's last consumer has switched. `ukp serve token list` shows labels, key
|
|
177
|
+
ids, and scopes (never hashes).
|
|
178
|
+
- **Chain order**: while `UKP_SERVE_TOKEN` is set, the env token wins and the
|
|
179
|
+
file is ignored — the env is the shortcut form of the same gate (the same
|
|
180
|
+
order as `AWS_*` env vars over `~/.aws/credentials`, or `GH_TOKEN` over the
|
|
181
|
+
stored gh login). An env var set to an empty value counts as unset.
|
|
182
|
+
|
|
183
|
+
The Windows resident form (`--print-task --write`) interpolates the env token
|
|
184
|
+
into `start-door.cmd`; a file-key door instead wants the cmd's `set
|
|
185
|
+
"UKP_SERVE_TOKEN=..."` line removed so the key file arms the gate.
|
|
186
|
+
|
|
118
187
|
## Windows host (https — resident form)
|
|
119
188
|
|
|
120
189
|
Windows has no socket-activation equivalent, so the https path on a
|
|
@@ -219,6 +288,13 @@ Pitfalls the verification run caught:
|
|
|
219
288
|
- `rg` on the host is the host's business — no ripgrep installed means
|
|
220
289
|
the remote `rg` capability reports unavailable (everything else works).
|
|
221
290
|
|
|
291
|
+
`ukp diagnose --door` is the read-only health check for this resident form:
|
|
292
|
+
for every door it reports the task state, the wscript->cmd->bun process tree,
|
|
293
|
+
the port (and who holds it), and the door.log tail — and exits 1 when a door
|
|
294
|
+
is down (a host with no doors is healthy). Each finding names its repair;
|
|
295
|
+
removal and restart moves live in the `ukp serve --print-task` §5 checklist
|
|
296
|
+
(the three-level teardown below) and the pitfalls above.
|
|
297
|
+
|
|
222
298
|
To remove the door — the inverse of steps 4–0, in three levels (stopping
|
|
223
299
|
is not disabling, and disabling is not erasing; `ukp serve --print-task`
|
|
224
300
|
prints this checklist as its §5):
|
|
@@ -237,11 +313,13 @@ prints this checklist as its §5):
|
|
|
237
313
|
plaintext** — deleting it destroys this copy; if it ever left the
|
|
238
314
|
machine, treat the token as burned), `start-door-hidden.vbs`, and
|
|
239
315
|
`door.log`, plus the certificate material: your `--tls-cert` /
|
|
240
|
-
`--tls-key` paths, or — with `--tls` — the identity
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
316
|
+
`--tls-key` paths, or — with `--tls` — the self-signed identity beside
|
|
317
|
+
the host registry: `%USERPROFILE%\.ukp\tls\<endpoint-name>\` for a
|
|
318
|
+
single-endpoint door, `%USERPROFILE%\.ukp\tls\` for a host door (it
|
|
319
|
+
never lives inside the knowledge folder, so git or cloud sync cannot
|
|
320
|
+
carry the private key out; `ukp unregister` leaves it in place on
|
|
321
|
+
purpose — a re-registered endpoint keeps its pin — so delete it by
|
|
322
|
+
hand when you are done). The endpoints stay registered
|
|
245
323
|
on the host for local use — `ukp unregister --endpoint <name>` per
|
|
246
324
|
endpoint only if you are done with them; and every consumer that
|
|
247
325
|
registered holds the token in plaintext in its own registry —
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@catheadowl/ukp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"bun": ">=1.3.14"
|
|
@@ -18,10 +18,12 @@
|
|
|
18
18
|
"keywords": [
|
|
19
19
|
"knowledge",
|
|
20
20
|
"cli",
|
|
21
|
-
"bun",
|
|
22
|
-
"qmd",
|
|
23
21
|
"search",
|
|
24
|
-
"agent"
|
|
22
|
+
"agent",
|
|
23
|
+
"ai-agents",
|
|
24
|
+
"knowledge-base",
|
|
25
|
+
"local-first",
|
|
26
|
+
"self-hosted"
|
|
25
27
|
],
|
|
26
28
|
"bin": {
|
|
27
29
|
"ukp": "bin/ukp.js"
|
|
@@ -30,16 +32,19 @@
|
|
|
30
32
|
"bin",
|
|
31
33
|
"src",
|
|
32
34
|
"docs",
|
|
35
|
+
"skills/ukp/SKILL.md",
|
|
33
36
|
"README.md",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
37
|
+
"SECURITY.md",
|
|
38
|
+
"LICENSE"
|
|
36
39
|
],
|
|
37
40
|
"scripts": {
|
|
38
41
|
"dev": "bun run src/cli.ts",
|
|
42
|
+
"prepack": "bun ./scripts/pack-audit.mjs",
|
|
39
43
|
"test": "bun test",
|
|
40
44
|
"typecheck": "tsc --noEmit"
|
|
41
45
|
},
|
|
42
46
|
"dependencies": {
|
|
47
|
+
"@any-nav/scan": "^0.2.0",
|
|
43
48
|
"commander": "15.0.0",
|
|
44
49
|
"smol-toml": "1.7.1",
|
|
45
50
|
"zod": "4.4.3"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ukp
|
|
3
|
+
description: "ukp — a CLI tool for named knowledge endpoints(registered local or remote folders)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ukp
|
|
7
|
+
|
|
8
|
+
A CLI tool for knowledge endpoints: register any local or remote folder
|
|
9
|
+
as a named endpoint, then address knowledge by name through one stable
|
|
10
|
+
command surface.
|
|
11
|
+
|
|
12
|
+
## Entry
|
|
13
|
+
|
|
14
|
+
Run `ukp --help` — the command surface teaches itself: grouped help,
|
|
15
|
+
per-command `--help` for exact grammar, `ukp guide <topic>` for
|
|
16
|
+
role-based walkthroughs, `ukp list` for the registered endpoints.
|
|
17
|
+
|
|
18
|
+
## Handoff: ukp:// references
|
|
19
|
+
|
|
20
|
+
Knowledge you got from ukp is handed off as
|
|
21
|
+
`ukp://<endpoint>/<path>` — name-addressed, so folders can move while
|
|
22
|
+
references stay valid.
|
|
23
|
+
|
|
24
|
+
`ukp read <uri>` opens one as-is, replayable in
|
|
25
|
+
any session. The cross-repo `{name}:path` shorthand resolves the same
|
|
26
|
+
way.
|
|
27
|
+
|
|
28
|
+
## Writes: propose
|
|
29
|
+
|
|
30
|
+
Endpoints are read-only by default. Changes go through `ukp propose` —
|
|
31
|
+
an idempotent suggestion box (created / unchanged / updated; a revision
|
|
32
|
+
bumps only on updated) that the endpoint owner adjudicates.
|