- Go 91.7%
- Shell 8%
- Dockerfile 0.3%
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>
|
||
|---|---|---|
| cmd | ||
| deploy | ||
| docker/apelido | ||
| e2e | ||
| node | ||
| .dockerignore | ||
| .gitignore | ||
| actions.go | ||
| actions_test.go | ||
| bench_test.go | ||
| child.go | ||
| child_test.go | ||
| doc.go | ||
| docker-compose.yml | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| handle.go | ||
| handle_test.go | ||
| hashstore.go | ||
| LICENSE | ||
| mutations.go | ||
| reader.go | ||
| reader_test.go | ||
| README.md | ||
| replay_test.go | ||
| snapshot_test.go | ||
| state.go | ||
| validator.go | ||
| void_test.go | ||
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 throughProcessMainChainBlock(a missing height is requested; a relay that does not hold it stops the node rather than letting it skip), and handlesSyncMsgChecksumConfirmandSyncMsgRollback. - Tier-2+ nodes (
upstream.kind: apelido) connect to an upstream apelido node via theSocialSyncMsgprotocol (0x40-0x46) and receive pre-filteredSubChainBlocks.
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:
- Environment variable (
private_key_env): raw 64-char hex private key. For dev/containers only. - Keystore file (
keystore): encrypted with scrypt. Password frompassword_envenv var or interactive prompt. - 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.tokenin each config must match the public token of the upstream node. Runapelido-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