arizuko

arizuko › howto › WebDAV mount

WebDAV

Every group folder is available over WebDAV at https://<host>/dav/<group>/. A WebDAV client that sends a Bearer token, for example rclone or curl, sees the same files that the agent reads and writes.

rclone mounts a group in four commands

Set up an rclone remote for your instance once, then mount a group:

# 1. point rclone at the WebDAV endpoint
rclone config create arizuko webdav \
    url=https://<host>/dav/ \
    vendor=other

# 2. on the host, mint a Bearer token that authd's key signs
arizuko token -i <instance> issue bearer <folder> --scope <scope> --sub <your-sub> --ttl 24h

# 3. give the token to rclone; it stores it in ~/.config/rclone/rclone.conf
rclone config update arizuko bearer_token=<paste-token-here>

# 4. mount a group as a local folder
mkdir -p ~/arizuko-atlas
rclone mount arizuko:atlas ~/arizuko-atlas --vfs-cache-mode writes

The files of the group are now under ~/arizuko-atlas/, for example CLAUDE.md, diary/, facts/ and media/. Edit them in your editor or add files. The agent reads the changes at its next turn.

A browser can show the files without setup. Sign in at /auth/login, then open https://<host>/dav/<group>/. dufs, the server under davd, shows a directory page with links.

A URL path is a path under groups/

The URL /dav/<group>/<rest> maps to groups/<group>/<rest> under the data directory. davd serves <DATA_DIR>/groups/ read-write. proxyd removes the leading /dav before it forwards the request, and it applies the write block of davAllow.

URLpath on the host
/dav/a 302 redirect to /dav/<first-group>/
/dav/atlas//srv/data/arizuko_<inst>/groups/atlas/
/dav/atlas/CLAUDE.md/srv/data/arizuko_<inst>/groups/atlas/CLAUDE.md
/dav/atlas/users/alice.md/srv/data/arizuko_<inst>/groups/atlas/users/alice.md

<group> is the folder name on disk, not the display name of the group. The dashboard lists the folders at /dash/groups/. The default group of a new instance is main.

The dashboard login also opens /dav/

The auth layer of /dash/ also protects /dav/. No separate WebDAV password and no Basic Auth exist.

clientcredentialhow
browserJWT cookiesign in at /auth/login; the refresh_token cookie also works for /dav/
rclone, curlBearer tokenmint it with arizuko token -i <instance> issue bearer and send Authorization: Bearer <token>

A request without a credential gets 303 See Other with Location: /auth/login. A browser follows the redirect. curl follows it only with -L. To check:

curl -s -o /dev/null -w '%{http_code}\n' https://<host>/dav/
# 303

curl -s -o /dev/null -w '%{http_code}\n' \
     -H "Authorization: Bearer $TOKEN" \
     https://<host>/dav/atlas/
# 200

The dashboard does not issue Bearer tokens. arizuko token -i <instance> issue bearer signs a JWT with the active key of authd, for the sub that --sub names. proxyd then reads the grants of that sub. No test opens /dav/ with such a token end to end.

A Bearer token is a password. A person with your token can read every group that the grants of its sub cover. Give it a short --ttl.

Grants decide which groups a user can open

Authentication tells proxyd who you are. Grants tell proxyd which group folders you can open. proxyd reads the grants for each request:

Operator
A user with the ** grant sees every group of the instance. /dav/ sends the operator to the first group in sorted order. Any other group opens when the operator types its path.
Other users
A user sees only the groups that an acl row allows. A request for another group returns 403 Forbidden, and proxyd logs dav forbidden with the sub and the path.

A login alone is not enough. A signed-in user without grants gets 403 on every group path.

proxyd blocks writes to logs and secret files

davd mounts <DATA_DIR>/groups/ read-write. The davAllow function of proxyd is the only place that blocks writes. The rules come from proxyd/main.go:

Reads always pass
The block never stops GET, HEAD, OPTIONS and PROPFIND. Auth and grants still apply.
Logs are read-only
Every write method on a logs segment of a group path returns 403: PUT, POST, MKCOL, DELETE, MOVE, COPY and PROPPATCH.
Secret files are read-only everywhere
A path with a segment .env or .git, or a segment that ends in .pem, refuses writes. Reads still work, so the agent can inspect them.
All other paths are writable
A PUT to /dav/atlas/notes/todo.md writes the file to disk, and the agent reads it at its next turn.

proxyd also checks the Destination header of MOVE and COPY with the same rules, so a move cannot write into a group or a path that the request line could not reach.

Four tasks show the use

An operator edits CLAUDE.md from a laptop

rclone mount arizuko:atlas ~/atlas --vfs-cache-mode writes
$EDITOR ~/atlas/CLAUDE.md
# save and unmount; the agent uses the new file at its next turn

A user gives the agent a file to read

curl -H "Authorization: Bearer $TOKEN" \
     -T ./quarterly.csv \
     https://<host>/dav/atlas/media/quarterly.csv

# then ask the agent in the chat: "read media/quarterly.csv and summarise"

The agent writes a file, and the user downloads it

The agent runs in a guest with its group folder shared read-write. It can write files, for example PDFs, charts or transcripts, everywhere except logs/. The user then downloads them:

curl -H "Authorization: Bearer $TOKEN" \
     -o report.pdf \
     https://<host>/dav/atlas/out/report.pdf

An operator lists the memory files of the users

The memory of each user is in <group>/users/<user>.md. With an operator grant, you can list all of them:

curl -X PROPFIND \
     -H 'Depth: 1' \
     -H "Authorization: Bearer $TOKEN" \
     https://<host>/dav/atlas/users/

Only clients that send a Bearer header can sign in

rclone

Make the remote in one command, without prompts:

rclone config create arizuko webdav \
    url=https://<host>/dav/ \
    vendor=other \
    bearer_token=$TOKEN

Mount with the VFS cache, so that the save-and-rename of an editor works:

rclone mount arizuko:atlas ~/atlas \
    --vfs-cache-mode writes \
    --dir-cache-time 30s

curl

# read a file
curl -H "Authorization: Bearer $TOKEN" \
     https://<host>/dav/atlas/CLAUDE.md

# list a directory; Depth: 1 gives the direct children only
curl -X PROPFIND -H 'Depth: 1' \
     -H "Authorization: Bearer $TOKEN" \
     https://<host>/dav/atlas/

# upload a file
curl -X PUT --data-binary @./notes.md \
     -H "Authorization: Bearer $TOKEN" \
     https://<host>/dav/atlas/notes.md

Finder, Windows Explorer and davfs2

These clients send a user name and a password with HTTP Basic auth. proxyd accepts only Authorization: Bearer <JWT> or the refresh_token cookie, so these clients cannot sign in. Use rclone: rclone mount gives a local folder that Finder and Explorer can open.

Six problems have a known cause

303 redirect to /auth/login
You are not signed in. In a browser, follow the redirect and sign in. With curl or rclone, send Authorization: Bearer <token>. arizuko token -i <instance> issue bearer makes the token.
403 Forbidden on a path that you can read
The write block applies, because you wrote under logs/ or to .env, a *.pem file or .git. Or you have no grant for that group. journalctl -u arizuko_<inst> | grep -E "dav (forbidden|blocked)" shows which one.
404 "WebDAV not configured"
proxyd has no /dav/ route, because WEBDAV_ENABLED=false or the web layer is off. WEBDAV_ENABLED defaults to true when WEB_PORT is set. Set it in .env, run arizuko generate and restart.
404 at /dav/<group>/
The group folder does not exist. Check the folder name in /dash/groups/.
423 Locked
Another WebDAV client holds a lock on the file. Disconnect that client or wait. dufs locks each file, and a lock can stay for some minutes after a hard disconnect.
The agent does not use an edit
The write arrived, because proxyd logged no 403. A new file does not start a turn, so send the agent a message. Or the file is in a path that the agent does not read. The CLAUDE.md of the group says what the agent reads, for example media/, facts/ and users/.

Only proxyd stands between the network and davd

TLS ends at the public edge in front of proxyd. Behind it, from proxyd to davd and from davd to the disk, traffic is plain HTTP on the Docker network. The network namespace is the trust boundary. davd checks no identity. It expects that every request has passed the auth check, davAllow and the grant check in proxyd. [SECURITY.md] has the full threat model.

Four pages give the details

[davd/README.md]
The daemon, a small wrapper around sigoden/dufs.
webdav
The same surface on one page.
auth
Where the Bearer token comes from.
grants
The limit to one group.