# aweft identity, v0

Status: draft. Normative for the parts marked so; the encoding is open.

Every observable carries an id. Ids are how two replicas agree on which observable a change
refers to without agreeing on where it sits in the tree.

## 1. Requirements

An id **MUST** be unique within a document, and **MUST** remain valid when the observable it
names is detached and reattached.

Ids **MUST** be generated from a cryptographically secure source. An implementation
**MUST NOT** provide a fallback to a non-cryptographic generator, and **MUST NOT** expose a
way to replace the generator at runtime.

*Rationale:* a global generator override exists to make tests deterministic, and the failure
it produces is that tests run on one source of randomness while production runs on another,
so no test can observe the weakness. Determinism in tests is obtained by injecting a
generator at construction, not by replacing a global.

## 2. Ids are not credentials

An id **MUST NOT** be used as an authentication token, session identifier, or capability.

*Rationale:* ids are published. Every id in a synchronized document is visible to every
participant, by design. A value that is broadcast cannot also be a secret. Credentials are
minted by a separate mechanism with a separate call site, so that reaching for the wrong one
is not expressible.

## 3. Encoding

An id is **12 bytes**, 96 bits from the source in section 1. Its textual form, for a URL, a
log line, or a key in a document, is **base64url with no padding: exactly sixteen
characters** drawn from `A-Z`, `a-z`, `0-9`, `-` and `_`.

Twelve bytes is a multiple of three, so the textual form carries no padding to strip and no
character needing an escape anywhere an id is likely to be written.

*Rationale, measured:* ids are 35.5% of the raw bytes of a representative editing stream, so
the width is the largest single lever on the size of the format. Measured on that stream
against 16 bytes, 12 bytes is 6.1% smaller compressed and 8 bytes is 11.8% smaller.

Twelve was chosen over eight because a collision is silent, permanent, and corrupts data
with no recovery path, and 64 bits reaches a probability at scales an application can
actually get to: a document of a million observables sits at roughly one in 37 million, and
a hundred thousand such documents at roughly one in 370. At 96 bits that same fleet is
around one in a trillion. Twelve was chosen over sixteen because sixteen buys nothing that
96 bits has not already bought, and costs 6.1% of every byte sent, forever.

Width defends against accidental collision only. A participant that wants a collision can
simply choose one, so no id width is a defence against a hostile writer. That belongs to the
authority layer, and it is why 96 bits is enough here.

### 3.1 Ordering

An id **MUST NOT** carry a timestamp or a counter, and **MUST NOT** be ordered by anything
but its bytes.

*Rationale:* every id in a synchronized document is visible to every participant by design,
so a timestamp component publishes the creation time of every object to everyone who can
read the document, including anyone it is shared with later. What a timestamp buys is
sortable storage keys, and storage keys are local, unshared, and free to carry one.

Array ordering does not use ids at all. See [`spec/format.md`](/spec/format) section 6.6.

## 4. Open

- Whether an id ever needs re-minting, and what a replica holding the old one should do.
