URI / Addressing Grammar
Every resource in TinyCloud is named by a single string of the form tinycloud:{did-suffix}:{space}/{service}[/{path}][?{query}][#{fragment}] — a space (owner DID + name) followed by a service and an optional path. This URI is the resource half of every capability; getting it exactly right is what makes invocations and delegation chains verifiable.
Role
The grammar is Layer 1 bedrock. It is parsed and emitted by ResourceId / SpaceId in tinycloud-auth/src/resource.rs and wrapped by the Resource enum in tinycloud-core/src/types/resource.rs. Because a resource string fully identifies which owner, which space, which service, which path, a node can authorize a request from the URI plus its signature chain alone — no lookup. This concept is the precise spelling of the addressing referenced by autonomic-space, capabilities, and attenuation.
Shape
Canonical ABNF (code-grounded)
The authoritative grammar is the comment block in tinycloud-core/src/types/resource.rs (the whitepaper Appendix B mirrors it with minor drift):
id = "tinycloud:" method-name ":" method-specific-id ":" name
resource = id "/" service [ "/" path ] [ "?" query ] [ "#" fragment ]
method-name = 1*method-char
method-char = %x61-7A / DIGIT ; lowercase letter or digit
method-specific-id = *( *idchar ":" ) 1*idchar
idchar = ALPHA / DIGIT / "." / "-" / "_" / pct-encoded
pct-encoded = "%" HEXDIG HEXDIG
name = 1*nchar
service = 1*nchar
path = *( segment "/" ) segment
segment = *pchar
pchar = nchar / ":"
nchar = unreserved / pct-encoded / sub-delims / "@"
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
query = *( pchar / "/" / "?" )
fragment = *( pchar / "/" / "?" )
The two parse targets
SpaceId(idonly):tinycloud:{did-suffix}:{name}. Parsed by splitting on the last:— everything before is the DID suffix, after is the space name. Must have no/, query, or fragment, must be URI-normalized, and the name must be non-empty (resource.rs:307-329). The DID is reconstructed asdid:{suffix}then canonicalized.ResourceId(fullresource): adds the service and optional path/query/fragment. Parsed (resource.rs:338-376) by requiring schemetinycloud, no authority, normalization, and a/in the path; it splitsspace/rest, thenservice/path. The leading{did-suffix}:{name}of the space part is recovered withrsplit_once(':').
Forms you will see
- Canonical / wire form (
Display):tinycloud:pkh:eip155:1:0xf39f…2266:default/kv/notes/photo.jpg. The DID-suffix is the base DID minus its leadingdid:(4 chars). - DID-relative
did-suffix: e.g.pkh:eip155:1:0xf39f…2266(an EIP-155 did:pkh) orkey:z6Mk…(an Ed25519 did:key). - Encryption network URN (a separate
Resource::Otherform, NOT a space URI):urn:tinycloud:encryption:{ownerDid}:{network}— see system-spaces and the encryption service.
Mechanics
A Resource is the enum TinyCloud(ResourceId) | Other(UriString) (core/src/types/resource.rs:14-20). The Other arm carries non-space URIs, chiefly the encryption-network URN. Scope containment is ResourceId::extends (auth/src/resource.rs:193): a child resource extends a base iff same space, same service, same fragment, and the child path is path-prefixed by the base path — where prefix means child.starts_with(base) and the base ends in /, or the boundary is a / (so notes does not extend not, but notes/a extends notes/). A base with no path is extended by any path; a base with a path is not extended by a child lacking one. This extends check is the engine of attenuation and is invoked during delegation verification.
EIP-155 addresses are checksum-canonicalized and did:pkh:eip155:… DIDs are normalized on parse (tinycloud-auth/src/identity.rs:canonicalize_did), so two URIs differing only by address case resolve to the same space (test vectors at resource.rs:455-470).
Relationships
Identifies an space + service + path; the {name} segment names a system space; the resource is half of a capability (the other half is the ability {namespace}.{service}/{action}); extends powers attenuation and delegation subset-checks; the did-suffix is a DID.
Example
tinycloud:pkh:eip155:1:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266:applications/kv/com.listen.app/transcript/2026-06-23.json
parses to: space …:applications (owner did:pkh:eip155:1:0xf39f…2266), service kv, path com.listen.app/transcript/2026-06-23.json. A capability over …:applications/kv/com.listen.app/ (note trailing slash) extends-covers it, so a delegation of that prefix authorizes a tinycloud.kv/get invocation on this exact key. (See example-listen.)
Status & drift
Shipped and stable; this is Sam's top documentation gap, now pinned to code. Drift vs the whitepaper Appendix B ABNF: the spec restricts space/service to ALPHA / DIGIT / "-" / "_", but the code's name/service are the looser nchar set (and Name/Service parsing currently does no validation — both FromStr impls are TODO stubs accepting any string, resource.rs:30-42,127-130). The spec also writes the DID-suffix split as fixed fields; the code recovers it positionally via rsplit_once(':'). Code is canonical. The ln:{chain}:{addr}:{name} "internal short form" claimed in older discovery notes was not found in current db.rs (canonicalize_did emits did:pkh:eip155:…, not ln:) — see contradictions.
Sources
tinycloud-node:tinycloud-core/src/types/resource.rs:90-111(ABNF +Resourceenum),tinycloud-auth/src/resource.rs:169-376(ResourceId/SpaceId,extends,FromStr, test vectors :455-470),tinycloud-auth/src/identity.rs(canonicalize_did)whitepaper:appendix/appendix-b-uri-abnf-grammar.md(spec ABNF; drift noted)