Files
narl-workspace/docs/wiki/API.md
T
nvrl c72b65fc38 search: a write reads only its folder, and the match is lit
`file_text::catch_up` read every file without a row, which after a write meant
walking every file node there is to find the one or two just put in. It now
takes the part of the tree to look in: an upload asks about the folder it went
into, an import about the folder it was poured into, an SFTP save about the
folder it landed in, a decrypt about what was decrypted. Only an import at the
root and the pass at start look at everything.

The excerpt of a file found by its text marks each matched word between
U+0002 and U+0003, which no text file has a use for, and the list draws them
lit. A cut through a match leaves an opening without its close, and the rest
is lit to the end rather than the marker shown.

The screenshots are taken again, so the search one shows it.
2026-10-21 12:53:37 +02:00

5.2 KiB

API

Everything except the login, the health check and the two share routes requires the session cookie; /api/capture takes a token instead. Media answers Range requests, so a video seeks, sealed or not.

method path purpose
GET /api/health public: up, and the database answers — for a supervisor
POST /api/login, /api/logout session; /api/logout?everywhere=true ends every one
GET /api/me current user
GET /api/sessions every signed-in browser, the one seen last first
DELETE /api/sessions/{id} sign another browser out, and lock the vault
GET/POST /api/spaces the spaces / create one
GET/POST /api/nodes children (?parent=) or search (?q=, ?space=; a file matched by its text has in_text and the words around the match as excerpt, each matched word between U+0002 and U+0003) / create, from a template with template_id
GET/PUT/DELETE /api/nodes/{id} read / update only the fields sent — name, body, created_at, has_board; with base_updated_at, refused with 409 once it was saved since / delete into the trash, subtree and all — or for good, for something already in it
GET/DELETE /api/trash what was deleted, the last first / empty it
DELETE /api/trash/{id} delete one thing in the trash for good
POST /api/trash/{id}/restore back where it was deleted from, or to its space when that folder is gone
GET /api/spaces/{id}/board the lists and cards of one space
GET /api/agenda open cards due within a week or in hand, on every board, whether the diary has today, its entries on_this_day in earlier years, and today in WORKSPACE_TIMEZONE, with its bounds
GET /api/diary/days?month=2026-10 the diary's entries in a month, each with its local day
GET /api/diary/around/{id} the entries before and after a diary entry, by date
GET /api/nodes/{id}/templates the templates of the space the node is in
POST /api/capture { text, to: "card" | "diary", space? }, with a session or Authorization: Bearer a token
GET/POST /api/tokens the capture tokens / make one, { name }, answered with the token this once
DELETE /api/tokens/{id} remove a capture token
POST /api/spaces/{id}/lists add a list
PUT/DELETE /api/lists/{id} rename / delete a list and its cards
POST /api/lists/{id}/cards add a card
PUT/DELETE /api/cards/{id} update (title, body, due, repeat, done, link) / delete; finishing a repeating card answers its copy as again
POST /api/cards/{id}/move to another list and place in it; again as above
POST /api/nodes/{id}/move reparent, and place among the new siblings
POST /api/nodes/move { ids, parent_id }: several into one folder, all or none
POST /api/nodes/{id}/import import a folder or zip (multipart) under this node
POST /api/import import an export at the root: directories become spaces
GET /api/resolve?path= the node behind a slug path, work/notes/monday
GET /api/nodes/{id}/revisions a document's earlier versions, the newest first
GET /api/nodes/{id}/revisions/{rev} one earlier version, with its text
GET /api/nodes/{id}/backlinks the documents that link to this one, with the line each does it on
POST/DELETE /api/nodes/{id}/share mint or rotate / revoke a share link
GET /api/export the whole workspace, boards and dates included, as a zip
GET/POST /api/backup Proton Drive mirror: status / run now
POST/DELETE /api/backup/login log the mirror in to Proton, body {"username","password"} / forget its session
POST /api/backup/login/totp, /api/backup/login/captcha the two-factor code {"code"}, or the solved human check {"token"}, a waiting login asked for
GET/POST /api/media list / upload (multipart)
GET/DELETE /api/media/{id} serve (with Range) / delete a file nothing in the tree owns
GET /api/spaces/overview every space with what was last written in it
POST /api/nodes/{id}/files upload files into this folder as file nodes (?encrypt=true seals them)
POST /api/nodes/{id}/inspect what a certificate, key or keystore file holds ({password} opens a protected one)
POST /api/nodes/{id}/encrypt, /decrypt seal a file, or a folder for good — what is in it and what lands later — or take it out
GET/POST/PUT /api/vault status / set the password once / change it
POST /api/vault/unlock, /api/vault/lock open / close the vault
GET/POST /api/ssh-keys the keys that may mount over SFTP / add one
DELETE /api/ssh-keys/{id} remove a key, which ends its mounts
POST /api/share/{token} public: read a shared document, body {"key"}; sets the share's media cookie
GET /api/share/{token}/media/{id} public: media inside a shared document, with that cookie