Files

A space has one folder tree. A file grant is a permission entry with the same actions as a table grant, and access.request is the call that asks for it. A missing name and a hidden name are both not found.

A tiny example

from adoor import Client

db = Client("https://app.adoor.run", token)
space = "…"
db.fs_write(space, body="hello\n", path="", name="note.txt")
print(db.fs_read(space, path="note.txt")["body"])

That writes one file of a few bytes and reads it back. token is a connection key, the same key the HTTP API sends as Authorization: Bearer. A key without a file grant for that path gets NOT_FOUND.

Quickstart

Install and sign in the way the Python guide describes. The calls below use that client. MCP and POST /api/operations/<name> take the same inputs.

fs_stat reads one name. fs_list reads one page of a directory. Neither writes an event.

db.fs_stat(space, path="note.txt")
db.fs_list(space, path="")

fs_mkdir creates a directory. fs_write creates or replaces a file of at most 32 KiB. Pass the generation from stat when you replace. fs_read returns the text when the bytes are UTF-8.

folder = db.fs_mkdir(space, name="lab")
note = db.fs_write(space, body="plate P1\n", path="lab", name="read.txt")
db.fs_read(space, path="lab/read.txt")["body"]

fs_move reparents a file. fs_remove deletes a file or an empty directory. Both take the generation from stat. A directory that still has children stays.

db.fs_move(note["id"], space, folder["id"], note["generation"])
db.fs_remove(space, inode_id=note["id"], expected_generation=note["generation"])

History and undo are the table calls. Pass the inode to read one file. Undo reverses one of your own changes.

events = db.history(inode_id=note["id"], space=space)
db.undo(events[0]["id"])

fs_diff compares two content ids, or two event ids, of one file.

A grant names resource, inodeId, and actions. inodeId may be * for the whole space. The actions are select, insert, update, delete, alter, and drop_table. Ask with access.request. That is the same call a table grant uses.

db.call("access.request", {
    "permissions": [{"resource": "file", "space": space, "inodeId": folder["id"], "actions": ["select", "update"]}],
    "reason": "Read and replace the lab notes.",
})

The call stores a request. It does not grant anything until a person approves it.

Tutorial

This walk keeps a lab note, reads who changed it, and puts it back.

Create the directory and the note.

folder = db.fs_mkdir(space, name="run-1")
note = db.fs_write(space, body="od600 0.42\n", path="run-1", name="curve.txt")

List the directory. The page is the names you can select. A name outside the grant is absent, not listed as forbidden.

page = db.fs_list(space, inode_id=folder["id"])

Replace the note. The generation from the write is the one stat would return.

replaced = db.fs_write(
    space, body="od600 0.48\n", inode_id=note["id"], expected_generation=note["generation"]
)

Read the file history. The newest event is the replacement. Undo it. The body is the first write again.

events = db.history(inode_id=note["id"], space=space)
db.undo(events[0]["id"])
db.fs_read(space, inode_id=note["id"])["body"]

Diff those two versions by their event ids. Text of at most 1 MiB on both sides comes back as a unified diff.

db.fs_diff(space, inode_id=note["id"], left_event_id=events[1]["id"], right_event_id=events[0]["id"])

Ask for a narrower key for a notebook that should only read this directory. The entry is a file grant. The call is the table grant call.

db.call("access.request", {
    "permissions": [{"resource": "file", "space": space, "inodeId": folder["id"], "actions": ["select"]}],
    "reason": "The notebook only reads this run.",
})

A key that has no file grant for curve.txt gets NOT_FOUND from fs_read, the same way a key with no table grant gets NOT_FOUND for a hidden table.

Delete the note when the run is done. An empty directory can be removed the same way.

current = db.fs_stat(space, inode_id=note["id"])
db.fs_remove(space, inode_id=note["id"], expected_generation=current["generation"])

This page as Markdown