NorthlightSDK
Guides

Files and documents

Download a file from a person's runtime with files.download, and read the person's public documents with memory.list and memory.get.

Download a file

runtime.files.download(path, options?) returns the file's raw bytes as a Fetch Response (200, or 206 for a range) with Content-Type, Content-Length and Content-Range, so you can pipe its body on. path is an absolute path on the person's runtime that your app's grant lets it read. Options: range (one Range header value such as bytes=0-0 or bytes=-500) and signal.

const response = await runtime.files.download('/apps/<your client id>/files/report.pdf');
res.setHeader('content-type', response.headers.get('content-type') ?? 'application/octet-stream');
await pipeline(Readable.fromWeb(response.body!), res);

A non-2xx answer is thrown as an APIError, never returned as bytes. A bad range is 416, invalid_request with param: 'range'. A body cut short mid-stream means the file could not be read to the end. What a path may reach depends on the grant: see Scopes and grants. The client your app gets from oauth.client(userId) exposes only download from files; reading, writing and listing are not available to apps.

Read the person's public documents

runtime.memory is a read-only view of the knowledge and preference documents the person owns (memory and the /v1/memory routes are the API's names for this). An app reads the person's public documents with memory.list and memory.get, and needs no scope to do it. It never sees a friends or private document and cannot tell one from a document that does not exist: both answer 404 not_found. The list is what the app's agents can read about the person, so an app can show the person exactly which of their documents it draws on.

const page = await runtime.memory.list({store: 'user'});
for (const item of page.items) console.log(item.name, item.preview);

const doc = await runtime.memory.get({store: 'user', name: page.items[0].name});
console.log(doc.body);

store is 'user' in this release; any other value answers 400 invalid_request with param: "store". An app whose grant holds crtr:memory:manage:user (a first-party app) sees every privacy level. Reading records nothing: no watch, no read event, no audit row. Both methods are GET requests and follow the SDK's retry rule for safe requests.

List

list({store, path?, kind?, limit?, cursor?}, options?) sends GET /v1/memory and returns {items, next_cursor}.

FieldMeaning
pathA folder: lists documents whose name starts with <path>/, at any depth
kindknowledge or preference; both when omitted
limit1 to 100; default 50
cursorThe previous page's next_cursor, with the same store, path and kind

Items are ordered by name. Each item is {name, folder, kind, preview, summary, privacy, revision, created_at, updated_at}: name has no user/ prefix, folder is the name up to its last / ("" at the top level), and updated_at is the current revision's time. Folders are not items, and deleted and unlisted documents are left out. next_cursor is null on the last page.

listAll(params, options?) takes the same fields without cursor and yields every item across pages, fetching the next page only as you iterate:

for await (const item of runtime.memory.listAll({store: 'user', path: 'work'})) render(item);

Get

get({store, name}, options?) sends GET /v1/memory/document and returns a list item's fields plus body, size, truncated and grantee.

  • body is the current markdown as stored; [[links]] are not resolved. A body over 1 MiB is cut to its longest whole-character prefix of at most 1 MiB, with truncated: true; size is always the full length in UTF-8 bytes.
  • grantee is app:<id> of the app whose agent wrote the first revision, or null when the person wrote it.
  • An absent, deleted, friends or private name rejects with NotFoundError (code: 'not_found'); a malformed field rejects with BadRequestError.

On this page