Basic ID protocol on top of brisa network
  • Go 91.7%
  • Shell 8%
  • Dockerfile 0.3%
Find a file
Ruben M. Damião 0365caf2d5 drops: a chain test below the validators, and readers that skip dropped heights
node/chain_test.go: a stand-in validator (brisa's relaytest), a real relay,
an apelido node, and a second one started after a drop, following the first.
The dropped height held a power of attorney and a Void by the attorney: after
the drop the attorney's next Void is refused, the state is the one without
the height — in the late node too — and the confirmed sub-chain, read as a
client does, carries nothing of it. Without brisa's 3bb3b26 (the gateway
announcing drops before the blocks they cover) the late node applied the
dropped grant.

The readers of the sub-chain did not know drops:

- apelido-load takes back the acceptances of a dropped height ("dropped",
  neither valid lost nor invalid refused), undoes what the load believed of
  them (a dropped revoke leaves the grant in place, so the attorney's Voids
  after it are not counted as invalid accepted), and does not judge a handle
  race one of whose sides was dropped.
- the e2e client skips the blocks of dropped heights.

bench_test.go: the base for any change to parsing — in ValidateSign the
parse is ~0.2 µs of ~137 µs, the rest the two ed25519 checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 23:08:40 -03:00
cmd drops: a chain test below the validators, and readers that skip dropped heights 2026-09-28 23:08:40 -03:00
deploy node: upstream.network, so the node photographs its checkpoint; state: three sets in parallel 2026-09-26 15:57:29 -03:00
docker/apelido node: follow a brisa relay; upstream.kind, and the CLI finally tracked 2026-09-25 19:54:39 -03:00
e2e drops: a chain test below the validators, and readers that skip dropped heights 2026-09-28 23:08:40 -03:00
node drops: a chain test below the validators, and readers that skip dropped heights 2026-09-28 23:08:40 -03:00
.dockerignore port from github.com/freehandle/handles/ compatible with brisa rewrite from github.com/freehandle/breeze/ 2026-04-12 16:55:14 -03:00
.gitignore e2e: the end-to-end run — validators, relay, and two apelido nodes 2026-09-25 20:23:54 -03:00
actions.go handles: Instagram's alphabet, and no two underscores in a row 2026-09-25 00:59:44 -03:00
actions_test.go restore the delegated Void; sign the whole instruction; today's brisa 2026-09-25 00:35:44 -03:00
bench_test.go drops: a chain test below the validators, and readers that skip dropped heights 2026-09-28 23:08:40 -03:00
child.go child: Carried.Instruction, the instruction without brisa's wallet layer 2026-09-27 20:32:00 -03:00
child_test.go child: Carried.Instruction, the instruction without brisa's wallet layer 2026-09-27 20:32:00 -03:00
doc.go child protocols: ChildData, Select and ParseCarried 2026-09-27 20:26:00 -03:00
docker-compose.yml port from github.com/freehandle/handles/ compatible with brisa rewrite from github.com/freehandle/breeze/ 2026-04-12 16:55:14 -03:00
Dockerfile port from github.com/freehandle/handles/ compatible with brisa rewrite from github.com/freehandle/breeze/ 2026-04-12 16:55:14 -03:00
go.mod restore the delegated Void; sign the whole instruction; today's brisa 2026-09-25 00:35:44 -03:00
go.sum port from github.com/freehandle/handles/ compatible with brisa rewrite from github.com/freehandle/breeze/ 2026-04-12 16:55:14 -03:00
handle.go handles: Instagram's alphabet, and no two underscores in a row 2026-09-25 00:59:44 -03:00
handle_test.go handles: Instagram's alphabet, and no two underscores in a row 2026-09-25 00:59:44 -03:00
hashstore.go replace hashVault with papirus.HashSet 2026-04-13 16:11:43 -03:00
LICENSE port from github.com/freehandle/handles/ compatible with brisa rewrite from github.com/freehandle/breeze/ 2026-04-12 16:55:14 -03:00
mutations.go restore the delegated Void; sign the whole instruction; today's brisa 2026-09-25 00:35:44 -03:00
reader.go restore the delegated Void; sign the whole instruction; today's brisa 2026-09-25 00:35:44 -03:00
reader_test.go handles: Instagram's alphabet, and no two underscores in a row 2026-09-25 00:59:44 -03:00
README.md node: follow a brisa relay; upstream.kind, and the CLI finally tracked 2026-09-25 19:54:39 -03:00
replay_test.go restore the delegated Void; sign the whole instruction; today's brisa 2026-09-25 00:35:44 -03:00
snapshot_test.go state on disk and state sync; bootstrap, and a default memory limit 2026-09-26 19:52:40 -03:00
state.go state on disk and state sync; bootstrap, and a default memory limit 2026-09-26 19:52:40 -03:00
validator.go restore the delegated Void; sign the whole instruction; today's brisa 2026-09-25 00:35:44 -03:00
void_test.go restore the delegated Void; sign the whole instruction; today's brisa 2026-09-25 00:35:44 -03:00

Apelido

Identity registry protocol for the brisa network.

Apelido is a sub-chain that filters the brisa block stream for identity operations and maintains a registry of members, handles (nicknames), and powers of attorney. It runs as a node/social.SocialNode parameterised on the apelido state machine.

Architecture

brisa validators ──blocks──> relay ──blocks──> apelido-node (tier-1)
                                                    │
                                          SocialSyncGateway
                                                    │
                                               apelido-node (tier-2+)
  • Tier-1 node (upstream.kind: relay) connects directly to a brisa relay and speaks its protocol: it subscribes for main-chain blocks, applies every height in order through ProcessMainChainBlock (a missing height is requested; a relay that does not hold it stops the node rather than letting it skip), and handles SyncMsgChecksumConfirm and SyncMsgRollback.
  • Tier-2+ nodes (upstream.kind: apelido) connect to an upstream apelido node via the SocialSyncMsg protocol (0x40-0x46) and receive pre-filtered SubChainBlocks.

Both tiers support automatic rollback: confirmed blocks are persisted, tentative blocks stay in memory, and state snapshots enable recovery on rollback.

State

The registry tracks three sets, each backed by a papirus HashStore:

Set Contents
Members Registered identity tokens (hashes)
Captions Claimed handle strings (hashes)
Attorneys Active (member, attorney) delegation pairs (hashes)

Actions

All actions are carried inside brisa.Void with Protocol = 0x4150454C ("APEL").

The author signs the whole instruction, brisa layer included: the Void header [0][IVoid][Epoch][Protocol] and the payload. Action.Serialize returns those bytes, and apelido.Dress(action, wallet, fee) turns them into a Void by appending the wallet, the fee and the wallet's signature — the wallet may be someone other than the author. Because the epoch is under the author's signature, a published action cannot be re-wrapped at another height: an action is good at its own height, a Void older than brisa's lapse is refused, and an identical one is deduplicated by hash.

Action Kind Description
JoinNetwork 0x01 Register a new identity and claim a handle
UpdateInfo 0x02 Update handle details (author or attorney signed)
GrantPowerOfAttorney 0x03 Delegate signing rights to another token
RevokePowerOfAttorney 0x04 Cancel a delegation
Void 0x05 Publish Data on a member's behalf, signed by the member or an attorney it granted — how protocols built on apelido speak for a member. Changes no state

Building

cd apelido
go build ./cmd/apelido-node/

Or with Docker (run from the parent directory containing both brisa/ and apelido/):

docker build -f apelido/Dockerfile -t apelido-node .

Quick Start

# Generate example config
./apelido-node -example > apelido-node.yaml

# Edit the upstream section with the relay token and address
# vim apelido-node.yaml

# Start the node (generates a keystore on first run)
export APELIDO_PASSWORD=yourpassword
./apelido-node -config apelido-node.yaml

Configuration

Flags

Flag Default Description
-config apelido-node.yaml Path to YAML configuration file
-example Print example configuration and exit
-help Show help message

YAML structure

node:
  db_path: "./data"           # Directory for sub-chain block store and apelido state
  db_name: "apelido"          # Database name prefix

upstream:
  kind: "relay"               # "relay" (brisa relay/validator) or "apelido" (another apelido node); required
  token: ""                   # 64-char hex token of the relay or upstream apelido node
  addr: "localhost:9200"      # Address of the upstream

sync:
  listen: ""                  # Port for serving downstream apelido nodes (empty = disabled)
  authorized_nodes: []        # Tokens allowed to sync (empty = open)

credentials:
  keystore: "./keystore.json"
  password_env: "APELIDO_PASSWORD"
  generate: true

reconnect:
  initial_delay: "1s"
  max_delay: "60s"
  multiplier: 2.0

Credentials

Three options, checked in order:

  1. Environment variable (private_key_env): raw 64-char hex private key. For dev/containers only.
  2. Keystore file (keystore): encrypted with scrypt. Password from password_env env var or interactive prompt.
  3. Auto-generate (generate: true): creates a new keystore if one doesn't exist.

Docker Deployment

Single node

# From the parent directory containing both brisa/ and apelido/
docker build -f apelido/Dockerfile -t apelido-node .

docker run -d \
  -e APELIDO_PASSWORD=yourpassword \
  -v ./config.yaml:/config/config.yaml:ro \
  -v ./data:/data \
  apelido-node -config /config/config.yaml

Multi-node with docker-compose

The included docker-compose.yml sets up two apelido nodes:

  • apelido-node1 (tier-1): connects to an external brisa relay
  • apelido-node2 (tier-2): syncs from apelido-node1
# Generate keystores first
export APELIDO_PASSWORD=testpass
./apelido-node -config docker/apelido/node1.yaml  # generates keystore on first run
./apelido-node -config docker/apelido/node2.yaml

# Start the stack
docker-compose up -d

Note: The upstream.token in each config must match the public token of the upstream node. Run apelido-node -config <file> once to see the generated token, then update the downstream config.

Testing

go test ./...

16 tests covering:

  • Action serialization/deserialization and signature verification
  • Validator preconditions (duplicates, missing prerequisites, delegation)
  • brisa Void wrapping and protocol filtering
  • Node construction and end-to-end block processing
  • Rollback end-to-end: join + grant + rollback + rejoin, verifying state consistency

Package Structure

apelido/
  actions.go          Four action types (join, update, grant, revoke)
  mutations.go        Per-block state diff container
  state.go            State (chain.Stateful): members, captions, attorneys
  validator.go        MutatingState (chain.Blocker): action validation
  reader.go           brisa.Void adapter layer
  hashstore.go        papirus HashStore wrapper
  doc.go              Package documentation
  node/
    node.go           SocialNode type alias and constructor
  cmd/
    apelido-node/
      main.go         CLI entry point
      config.go       YAML configuration