Version 0.2 (draft) · 2026-06-22
Webpath is an open protocol for URL-path-first file hosting where AI agents are the
primary clients. The whole surface is plain HTTP: any client that can GET/PUT/PATCH/
DELETE a URL can read, write, share, and discover files — no SDK, no client library,
no GUIDs, no runtime documentation.
This document is the normative contract. It is implementation-neutral: it describes the
wire behavior, not a stack. Anyone can implement it. knower.cc is the
reference implementation (Appendix A).
The keywords MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as in RFC 2119. A conformance test for every normative rule is in the Conformance Guide.
/api prefix on the data plane and no opaque identifiers.GET reads, PUT writes, PATCH edits metadata or appends, DELETE deletes, HEAD returns metadata in headers.alice.example.com); the storage key is lowercase(host) + pathname. No path segment is ever a space reference.?visibility.application/problem+json carrying a full-sentence, agent-relayable recovery hint./join); a space host serves one space's files.https://<space-host>/<path>./-separated, case-sensitive, no .. traversal. A trailing / denotes a directory.pth_) — a signed bearer token scoped to a space, a path prefix, and a set of ops... or . segments, no \. Traversal attempts (including percent-encoded) MUST be rejected after decoding.token, meta, list, browse, visibility, append, prepend, confirm, qr, links, audit, search (alias q), og, replace, reindex, after.llms.txt, any basename beginning _ (_config.json, _links.json, _audit, _stats, …), and .well-known/*. A _-prefixed basename MUST NOT be user-writable.GET /<path> read content (clean URL renders; extension = source)
GET /<path>?meta metadata as JSON
GET /<dir>/ list a directory (trailing slash) — ?list forces JSON
HEAD /<path> metadata in response headers
PUT /<path> create or replace (body = content)
PUT /<path>?append append body · ?prepend prepend body
PATCH /<path>?meta merge a partial metadata document
PATCH /<path>?replace in-place fragment edit of an HTML file (MAY)
DELETE /<path> delete (a directory/space requires ?confirm=<host>)
Status codes a conformant host MUST use: 200 (ok/replace), 201 (created), 204
(deleted / empty success), 206 (range), 304 (not modified), 400 (bad request),
401 (auth required/failed), 403 (out of scope / forbidden), 404 (not found —
also the response for a private file to an unauthorized reader), 405 (method not
allowed on a generated path), 409 (conflict — name taken / not-appendable), 412
(precondition failed), 413 (too large), 415 (file type not allowed), 422
(unprocessable — bad name/scope/field), 429 (rate limited).
GET /<path>?meta MUST return JSON including at least:
{
"path": "/notes/standup.md",
"size": 1843,
"content_type": "text/markdown; charset=utf-8",
"etag": "\"a1b2c3\"",
"created_at": "2026-01-15T09:00:00Z",
"updated_at": "2026-01-17T11:23:00Z",
"visibility": "private", // private | unlisted | public
"name": "Standup notes", // optional, ≤120 chars
"description": "Weekly standup …", // optional, ≤500 chars
"tags": ["standup"],
"sharing": { "...": "..." }, // shown only to viewers with standing (§7.4)
"you": { "ops": ["read","write"] } // the viewer's effective ops, if any
}
PATCH /<path>?meta MUST accept a partial document and merge it. Server-managed fields
(size, etag, created_at, updated_at, path) MUST be immutable; an attempt to set
one MUST return 422 field-not-writable. name/description/tags/visibility are
writable. sharing is writable under a stricter gate (§7.4).
Three visibilities: private (owner + key/grant holders), unlisted (anyone with the URL; hidden from listings), public (readable and listed).
404, byte-identical body. A host MUST NOT leak the existence of a private file — including via the clean-URL render fallback (§10).?visibility=) and changeable later via PATCH ?meta.No passwords. POST /api/v1/auth/otp {email} MUST always return 204 (no account
enumeration) and rate-limit per email and per IP. POST /api/v1/auth/verify {email,code}
MUST exchange a correct code for a session and create the account on first verify. Codes
MUST expire and lock after a bounded number of attempts.
A session MUST be carried as a host-scoped cookie (__Host--prefixed: Secure, Path=/,
no Domain) so it cannot be sent to sibling spaces. Cookie-bearing writes SHOULD be
CSRF-guarded; bearer keys carry no ambient authority and need no such guard.
pth_)A key is a signed JWT verifiable against the host's .well-known/jwks.json. It is scoped:
{ "namespaces": ["alice.example.com"], // exactly the space(s) it works on
"ops": ["read","write","delete"], // ⊆ read write delete share subscribe (never admin)
"paths": ["/notes/"], // prefixes; ["/"] = whole space
"formats": ["md"] } // optional write-format allowlist
A key MUST be rejected (403 out-of-scope) outside its namespace, path prefix, ops, or
formats, and MUST NOT reach the control plane. The secret MUST be returned once and never
re-shown. Revocation MUST take effect within a bounded, advertised window.
Carriage. A key is sent as Authorization: Bearer <key> or ?token=<key>. When both
are present and disagree, the host MUST reject with 400. Responses to ?token= requests
MUST be Cache-Control: private, no-store, and the token MUST NOT appear in any response
body, header (Location/Content-Location), or links.
A host MAY support identity-bound grants: the owner writes a sharing map (keyed by email,
or "*" for any signed-in account) via PATCH ?meta on a file or directory, granting
read / write / admin on that path. Grants MUST NOT be shown to anonymous callers, MUST
NOT leak secrets, and are revoked by deleting the entry. A presented key MUST take precedence
over a session and report its own verdict.
PUT /<path>?append (or ?prepend) extends a file without a read-modify-write round-trip.
201). Both flags on one request MUST be 400.If-Match, the host MUST make exactly one attempt (412 on failure).csv tsv jsonl ndjson txt log md), the host MUST insert a single \n at the join when the preceding tail lacks one. Other types are byte-pure concatenation.json, html, XML family, yaml, toml) and binary types MUST refuse with 409 not-appendable.jsonl line a complete JSON value; csv fields closed). A violation MUST be 400 invalid-append-body with nothing written.ETag.If-Match on PUT/PATCH MUST prevent lost updates (412 on mismatch); If-None-Match: * MUST enforce create-only.If-None-Match on GET MUST return 304 for unchanged content.Range: bytes=…) SHOULD be supported (206).<path>.md (render to HTML) then <path>.html (serve as text/html), and MUST set Content-Location to the backing file. An explicit .md MUST be served as text/markdown and .html as text/plain — source, never executed.<script>, event handlers, javascript: URLs stripped). Source bytes stay verbatim via the extension URL.[[wikilinks]] to clean-URL links during render; if it does, a public render MUST NOT reveal the location of a private target.These are files. A host SHOULD synthesize them on read (never store them) so they cannot drift.
| Path | Behavior |
|---|---|
/llms.txt | The agent skill file: ≤1,500 bytes, text/plain, no auth, naming the real host and teaching every verb. MUST be read-only (405 on write). |
/_config.json | Config projection (read); PATCH edits only the editable fields (uploads policy, browse defaults). Server-owned fields → 422. PUT → 405. |
/_links.json | Owner-only links index (outgoing/incoming/broken/orphan). POST ?reindex rebuilds in bounded batches. PUT → 405. |
/_audit, /_stats | Owner-only activity/visitor feeds, if implemented. MUST NOT expose actors or private paths to anonymous callers. |
/.well-known/jwks.json | Public key set verifying pth_ keys. |
/.well-known/openapi.json | Machine-readable contract (platform hosts; OPTIONAL). |
Every non-2xx MUST be application/problem+json:
{ "type": "https://<errors-namespace>/etag-mismatch",
"title": "Precondition failed",
"status": 412,
"detail": "The file changed since you last read it. Re-read it and retry your write." }
type MUST be a stable absolute URI that SHOULD dereference to human-readable documentation
of the error. The reference registry of Webpath error types lives at
/a/webpath/errors. detail MUST be a full sentence a non-technical
user (or an agent relaying to one) can act on without external docs.
A host claiming Webpath conformance MUST pass every L1 Core case in the
Conformance Guide; L2 Full adds the reserved-path projections,
append, QR, and search. The privacy floor (WP-V2, WP-V5, WP-E2, WP-T2) is
non-negotiable.
knower.cc implements this spec as its native API. Its stack — one of
many valid choices — is: Cloudflare Workers runtime, ElysiaJS (TypeBox schemas) for
routing, R2 (S3-compatible) for bytes, D1/SQLite for metadata, Ed25519 JWT pth_
keys verified via JWKS. The protocol prescribes none of this; see the
generator prompt to build a conformant server on your own stack.
Real-time sync (WebSocket/SSE), server-side media transforms, versioning/restore, subscriptions, custom-domain onboarding, and a local sync daemon are layered specs, not part of the core contract.
→ API Reference · Conformance · Generate a server · Webpath home