Specs
spec/09-identity-credentials.md · shown exactly as it appears in the repository
09 — Agent Access & Credentials
Status: Draft, v0.1.
License: CC-BY-4.0 — see LICENSE-DOCS.
Overview
Every other chapter of this spec describes what happens between two parties: establishing a connection, controlling what crosses it, and sharing content over it. This chapter is different, it describes how a single party gets access to their own Bonafide Agent in the first place, before any connection or content exchange is possible. It is deliberately scoped outside the two-layer model: it is neither a Layer 1 concern (nothing here is visible to, or negotiated with, another party) nor a Layer 2 concern (it is not content, and is never shared).
An implementation MUST treat a credential purely as a local access-control
mechanism between a party and their own Agent. A credential MUST NOT be
exposed over any connection, MUST NOT appear in any message envelope, and
MUST NOT be usable, by a router, a peer, or anyone else as a handle to
correlate one of a party's connections with another. The non-correlation
guarantee in 00-overview.md is unaffected by anything in this chapter:
a credential exists so a party can reach their own database, not to
identify that party to anyone else.
Database isolation
Each party's Bonafide Agent-hosted data: content, connections, subscriptions, and the credentials described below, lives in its own database, isolated from every other party's. An implementation MUST NOT hold more than one party's data in a shared database or table that a single query could span across parties.
This isolation is what makes the "no central identity or accounts"
guarantee (00-overview.md) hold in practice, not just at the
wire-protocol level: there is no central user table to breach, enumerate,
or correlate across parties, only as many independent databases as there are parties.
A credential (below) is stored inside this same isolated database, alongside the party's content. There is no separate, central authentication store. Compromising one party's credential requires compromising that specific party's database; it reveals nothing about any other party's.
Credential records
A credential is the (username, password) pair a party presents to their
own Agent to gain access to their database. It is unrelated to, and
shares no key material with, the keys a connection generates for itself
(02-connections.md), a credential gets a party into their database; it
plays no role in anything that happens between parties once they're in.
A credential record is scoped to a single app (see "One credential set per app," below), and is stored, per the isolation requirement above, in the party's own database rather than in any datastore shared across parties.
One credential set per app
A single party's database MAY hold more than one credential record, one per app that has been added to that party's account. Each app's credential is independent of every other app's sharing the same database:
- Creating a credential for a new app MUST NOT require or disturb any credential already present for another app.
- Updating or replacing one app's credential (e.g. a password change) MUST NOT delete, invalidate, or otherwise alter any other app's credential record in the same database.
- Removing one app's credential MUST NOT remove another app's.
This is what lets a party use more than one independently developed app against the same domain underlying account and database, each app manages its own credential, and none of them can lock another app's party out as a side effect.
Password storage
An implementation MUST store a credential's password hashed with a cryptographically appropriate algorithm, salted with a value unique to that credential record. An implementation MUST NOT store or log a credential's password in plaintext, in any form, at any point.
An implementation MUST record, alongside each credential, enough information about the hashing parameters used to produce it (at minimum, the algorithm and work factor/iteration count) to recognize when a credential was hashed under older parameters. On a successful authentication against an outdated credential, an implementation MUST transparently re-hash the password under current parameters before completing the operation. This exists specifically so that strengthening hashing parameters over time, raising an iteration count, moving to a stronger algorithm, never silently invalidates every credential created before the change; without it, a parameter change becomes indistinguishable from a mass credential revocation.
Provisioning a party's database
Every credential record above presupposes a database for it to live in. Creating that database, and the mechanism by which a party signs up for one in the first place, is the responsibility of whatever organization hosts the Agent on the party's behalf, a hosting concern, not a protocol concern. This chapter requires only that a hosting organization provide some mechanism for creating and maintaining a party's database and facilitating their signup; it does not define what that mechanism looks like.