stash
This commit is contained in:
parent
25cef95711
commit
7d7730354e
8 changed files with 381 additions and 39 deletions
|
|
@ -20,4 +20,97 @@ uses it to verify access to the friend's inventory. While accepting a friend req
|
|||
their own public key to the friend's server. This way both users can access each other's inventory.
|
||||
|
||||
The protocol is based on a simple HTTPS API exchanging JSON data that is signed with the user's private key. By default
|
||||
Toolshed servers provide a documentation of the API at [/docs/api](/docs/api).
|
||||
Toolshed servers provide a documentation of the API at [/docs/api](/docs/api).
|
||||
|
||||
## Unique Handles
|
||||
|
||||
Federation only works if every server can talk about the same thing without a central authority to ask. Toolshed's
|
||||
answer is that every kind of entity that needs to be referenced across servers gets a handle: a name that is unique
|
||||
within its own scope and that carries, as part of itself, enough information to say where it is authoritative. This
|
||||
keeps servers independent of each other while still letting them agree on what they're talking about.
|
||||
|
||||
### Users (and Groups)
|
||||
|
||||
A user's handle is their username paired with the domain their account belongs to, written the way an email address
|
||||
is, e.g. `user@toolsheddomain.tld`. Uniqueness is only required within a single domain, not across all of Toolshed,
|
||||
so two different domains can each have their own "alice" without conflict, the same way two different email
|
||||
providers can each have an "alice" mailbox. The domain half of the handle is what makes the name globally
|
||||
unambiguous, and it is also what tells any other backend where to look to find out who's currently authoritative for
|
||||
that identity, i.e. which backend holds the account and can vouch for its public key.
|
||||
|
||||
Groups aren't implemented yet, but they're intended to fit the same idea: a group would get its own handle on the
|
||||
domain of the server that hosts it, the same way a user does, so that group membership and group-owned data could be
|
||||
referenced by other servers without needing a separate mechanism. A group handle is written with a leading `#`, e.g.
|
||||
`#groupname@toolsheddomain.tld`, so that group and user handles occupy visibly distinct spaces on the same domain
|
||||
and a name can't be squatted as one to collide with the other. See [groups.md](design-in-progress/groups.md) for
|
||||
details.
|
||||
|
||||
### Servers
|
||||
|
||||
The domain half of a handle, e.g. `toolsheddomain.tld`, is an authority record, not a location. Owning a domain
|
||||
just means being able to say which backend is currently authoritative for handles under it; it says nothing about
|
||||
where that backend is hosted, who operates it, or how many other domains it might also be authoritative for. A
|
||||
single backend can just as easily host entities for one domain or for many unrelated ones at once, there's no
|
||||
assumption anywhere in the model that a domain and a backend are the same thing, or that the relationship is one to
|
||||
one.
|
||||
|
||||
The frontend application is a third, separate thing again. The app a user loads isn't necessarily served by, or
|
||||
even related to, the backend that ends up handling their requests: when given a handle, the frontend looks up which
|
||||
backend is currently authoritative for that handle's domain and talks to that backend directly from then on. So
|
||||
using the frontend at one domain to log into a backend authoritative for a completely different domain isn't a
|
||||
special case, it's the normal path, since "where the app was loaded from" and "which backend answers for a given
|
||||
handle" were never the same question to begin with. Servers, in the cryptographic sense described below, don't have
|
||||
an identity of their own beyond the handles they're currently authoritative for; a backend is, conceptually, just
|
||||
wherever a given domain's handles happen to resolve to right now.
|
||||
|
||||
### Tags, Properties, and Categories
|
||||
|
||||
Inventory items aren't just described in free text, they can be classified with tags, properties, and categories,
|
||||
and those get handles too, written as an origin followed by the kind and name, e.g. `origin#tag:drill` or
|
||||
`origin#category:power-tools`. This lets the same short name (e.g. a "drill" tag) exist independently under
|
||||
different origins without colliding, while a handle as a whole unambiguously says which taxonomy an entry belongs
|
||||
to.
|
||||
|
||||
An origin isn't necessarily a server; it's whatever the classification is considered to have come from, which could
|
||||
be a shared, canonical reference dataset that multiple servers import and reuse, just as easily as it could be a
|
||||
server's own locally-invented taxonomy. This lets independently-run servers converge on a shared vocabulary where it
|
||||
matters, without forcing every server to invent its own from scratch or requiring a central body to define one.
|
||||
|
||||
Handles are resolved strictly: a reference to an origin or entity a server doesn't know about is left unresolved
|
||||
rather than being guessed at or silently merged into something that looks similar. This mirrors the rest of
|
||||
Toolshed's federation philosophy, nothing is combined across servers implicitly; agreement always has to be
|
||||
traceable to an explicit, shared handle.
|
||||
|
||||
### Items
|
||||
|
||||
Inventory items don't get a handle of their own the way tags or categories do, because they don't need one: every
|
||||
item belongs to exactly one user, so a simple local identifier is already enough to tell two items apart within that
|
||||
user's inventory. Combined with the owner's user handle, that local identifier is automatically unique across all of
|
||||
Toolshed too, since no two users share a handle. Unlike a tag or category, an item isn't meant to be the same entity
|
||||
reused across servers, it describes something one specific person actually owns, so there's no shared-origin concept
|
||||
to design for here, ownership alone already provides the scope.
|
||||
|
||||
## Cryptography
|
||||
|
||||
Handles say who or what is being referred to; cryptography is what lets a server trust that the entity behind a
|
||||
handle really is who it claims to be, without needing to ask a central authority. Every user handle has exactly one
|
||||
asymmetric keypair backing it: a private key that never leaves the user's control, and a public key that gets handed
|
||||
out freely as part of establishing that handle elsewhere.
|
||||
|
||||
A server first learns a public key at the moment it has reason to trust it: for its own users, that's registration;
|
||||
for a friend's handle, that's the friend-request/accept exchange described above. From then on, a public key is
|
||||
permanently paired with the handle it arrived with, never with a server. This is why friending is really a
|
||||
key-exchange ceremony rather than just a social action, accepting a request is the moment a server starts trusting a
|
||||
new handle's signature.
|
||||
|
||||
Every request made on a user's behalf is signed with that user's private key, and whichever server receives it
|
||||
verifies the signature against the public key it holds for that handle. This is what makes it safe for a request to
|
||||
travel to a server that isn't the user's home server: the receiving server doesn't need to trust the network path or
|
||||
the sender, only the signature.
|
||||
|
||||
It's worth being explicit about what this layer of cryptography is for and what it isn't. Signing establishes
|
||||
authenticity and integrity, that a request genuinely came from the handle it claims to, unaltered, not
|
||||
confidentiality. The data itself isn't encrypted by the protocol; keeping it private in transit is what the
|
||||
underlying HTTPS layer is for. Only user handles carry a keypair; tags, categories, properties, and item handles are
|
||||
just names, their trustworthiness comes entirely from being reachable only through a signed request from the user
|
||||
handle that owns or created them, not from any cryptographic identity of their own.
|
||||
Loading…
Add table
Add a link
Reference in a new issue