9.3 KiB
azimuth-watcher-ts
A comprehensive blockchain indexing and querying system for Azimuth, Urbit's identity layer on Ethereum. This system monitors multiple Ethereum smart contracts that make up the Azimuth PKI and provides a unified GraphQL API for querying Urbit identity information.
What is Azimuth?
Azimuth is Urbit's public key infrastructure (PKI) that lives on Ethereum. It's a set of smart contracts that manage Urbit identities called "points" (similar to usernames), their ownership, cryptographic keys, and hierarchical relationships. Each Urbit identity is represented as an NFT on Ethereum, making it decentralized and censorship-resistant.
What are Blockchain Watchers?
Blockchain watchers are services that continuously monitor smart contracts on Ethereum, index their events and state changes, and provide efficient APIs for querying blockchain data. Instead of directly querying the Ethereum blockchain (which is slow and expensive), applications can query watchers for fast, indexed access to current and historical blockchain state.
System Architecture
This system consists of 8 specialized watchers that each monitor different Azimuth smart contracts:
- azimuth-watcher - Core identity operations (ownership, keys, sponsorship)
- censures-watcher - Reputation and censure system
- claims-watcher - On-chain claims and metadata
- ecliptic-watcher - Galaxy operations and governance
- polls-watcher - Voting and governance proposals
- conditional-star-release-watcher - Conditional star distribution
- linear-star-release-watcher - Linear star distribution
- delegated-sending-watcher - Delegated operations
Plus a gateway server that provides a unified GraphQL endpoint routing queries to the appropriate watcher.
Hosted Service
A public instance is available at: https://azimuth.dev.vdb.to/graphql
You can also run the system locally using Stack Orchestrator.
Common Use Cases
Querying Urbit Point Information
The azimuth-watcher is the primary service for querying Urbit identity data. Here are the most common operations:
1. Get Point Owner and Basic Info
# Check who owns a specific Urbit point
curl 'https://azimuth.dev.vdb.to/graphql' \
-H 'Content-Type: application/json' \
--data-raw '{
"query": "{
azimuthGetOwner(
blockHash: \"latest\",
contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\",
_point: 1234
) {
value
}
}"
}' | jq
2. Get Cryptographic Keys for a Point
# Get encryption and authentication keys for networking
curl 'https://azimuth.dev.vdb.to/graphql' \
-H 'Content-Type: application/json' \
--data-raw '{
"query": "{
azimuthGetKeys(
blockHash: \"latest\",
contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\",
_point: 58213
) {
value {
encryptionKey: value0,
authenticationKey: value1,
cryptoSuiteVersion: value2,
keyRevisionNumber: value3
}
}
}"
}' | jq
# Example Response:
# {
# "data": {
# "azimuthGetKeys": {
# "value": {
# "encryptionKey": "0xc248f759474b16192bd8bdca0bff1b8bff555cd3d118022095331d6d98690c6d",
# "authenticationKey": "0x21188bac08542730e1c4697636d6fa25968f404470ccf917756f05e28c69045a",
# "cryptoSuiteVersion": "1",
# "keyRevisionNumber": "1"
# }
# }
# }
# }
3. Check Point Status
# Check if a point is active (booted) and has a sponsor
curl 'https://azimuth.dev.vdb.to/graphql' \
-H 'Content-Type: application/json' \
--data-raw '{
"query": "{
azimuthIsActive(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 1234) { value }
azimuthHasSponsor(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 1234) { value }
azimuthGetSponsor(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 1234) { value }
}"
}' | jq
4. Get All Points Owned by an Address
# Find all Urbit points owned by an Ethereum address
curl 'https://azimuth.dev.vdb.to/graphql' \
-H 'Content-Type: application/json' \
--data-raw '{
"query": "{
azimuthGetOwnedPoints(
blockHash: \"latest\",
contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\",
_whose: \"0x1234567890123456789012345678901234567890\"
) {
value
}
}"
}' | jq
Understanding Query Parameters
All queries require these standard parameters:
blockHash
: Use"latest"
for current state, or a specific block hash for historical queriescontractAddress
: Azimuth contract address (0x223c067F8CF28ae173EE5CafEa60cA44C335fecB
)_point
: The Urbit point number you're querying_whose
: Ethereum address when querying by owner
Available Query Types
Azimuth Watcher (Core Identity Operations)
Point Ownership & Information:
azimuthGetOwner
- Get point owner addressazimuthGetOwnedPoints
- Get all points owned by an addressazimuthIsActive
- Check if point is activatedazimuthIsLive
- Check if point is online
Cryptographic Keys:
azimuthGetKeys
- Get encryption/authentication keysazimuthGetKeyRevisionNumber
- Get key version number
Sponsorship Hierarchy:
azimuthGetSponsor
- Get point's sponsorazimuthGetSponsoring
- Get points sponsored by a pointazimuthHasSponsor
- Check if point has sponsor
Proxies & Permissions:
azimuthGetManagementProxy
- Get management proxy addressazimuthCanManage
- Check management permissionsazimuthGetSpawnProxy
- Get spawn proxy addressazimuthCanSpawnAs
- Check spawn permissions
Other Watchers
Censures - Reputation system queries with prefix censures
Claims - Metadata and claims queries with prefix claims
Ecliptic - Galaxy operations queries with prefix ecliptic
Polls - Governance voting queries with prefix polls
Star Release - Token distribution queries with prefixes linearStarRelease
and conditionalStarRelease
Delegated Sending - Delegation queries with prefix delegatedSending
Multi-Watcher Query Example
{
# Check point status (azimuth-watcher)
azimuthIsActive(blockHash: "latest", contractAddress: "0x223c067F8CF28ae173EE5CafEa60cA44C335fecB", _point: 1) {
value
}
# Check censure count (censures-watcher)
censuresGetCensuredByCount(blockHash: "latest", contractAddress: "0x325f68d32BdEe6Ed86E7235ff2480e2A433D6189", _who: 1) {
value
}
# Find a claim (claims-watcher)
claimsFindClaim(blockHash: "latest", contractAddress: "0xe7e7f69b34D7d9Bd8d61Fb22C33b22708947971A", _whose: 1234, _protocol: "text", _claim: "hello world") {
value
}
}
## Real-time Updates
Subscribe to blockchain events in real-time:
```graphql
subscription {
onEvent {
event {
__typename
... on OwnerChangedEvent {
point
owner
}
... on ActivatedEvent {
point
}
... on ChangedKeysEvent {
point
encryptionKey
authenticationKey
}
}
block {
hash
number
timestamp
}
}
}
Development Setup
Prerequisites
- Node.js 16+
- PostgreSQL database
- Ethereum RPC endpoint (or ipld-eth-server)
Quick Start
-
Clone and install dependencies:
git clone https://github.com/cerc-io/azimuth-watcher-ts.git cd azimuth-watcher-ts yarn install
-
Build all packages:
yarn build
-
Configure environment:
- Copy
packages/azimuth-watcher/environments/local.toml
and update database/RPC settings - Set up PostgreSQL databases for each watcher
- Copy
-
Start the azimuth-watcher:
cd packages/azimuth-watcher yarn server:dev # GraphQL server yarn job-runner:dev # Blockchain indexer (separate terminal)
-
Start the gateway server (optional):
cd packages/gateway-server yarn server:dev
Commands
Build and lint:
yarn build # Build all packages
yarn lint # Lint with zero warnings tolerance
Individual watcher operations:
yarn server:dev # Development GraphQL server
yarn job-runner:dev # Event processing worker
yarn fill # Backfill historical data
yarn reset # Reset watcher state
yarn checkpoint # Create state checkpoint
Architecture Details
Data Flow
- Indexing: Job runners continuously fetch Ethereum events and blocks
- Processing: Events are processed and state changes stored in PostgreSQL
- Querying: GraphQL servers provide indexed data with historical query support
- Gateway: Unified endpoint routes queries to appropriate specialized watchers
Storage
- Each watcher maintains its own PostgreSQL database
- State is checkpointed periodically for recovery
- IPLD (Content-addressed storage) support for cryptographic proofs
Generate Watchers
Follow instructions to generate Azimuth watchers using the code generator (@cerc-io/codegen
)