Join our community of builders on Discord!

Worker API

An event-driven indexer and API layer for Lightchain AI worker activity. It watches on-chain events, stores derived entities in PostgreSQL, and serves the indexed data through Express and GraphQL.

Architecture Overview

At runtime, the service works like this:
CodeTEXT
Startup flow:
  1. src/index.ts loads environment variables and the GraphQL schema.
  2. A Checkpoint instance is created with src/overrides.json.
  3. The EVM indexers are registered.
  4. Checkpoint starts polling configured contracts.
  5. Express serves REST routes under /api and GraphQL at /.

What The Service Indexes

The current indexer watches three contracts:
  • WorkerRegistry
  • JobRegistry
  • AIConfig
Current network config in the repository:
ParameterValue
Chain ID8200
Chain nameLightchain AI
Native tokenLCAI
Fetch interval12_000ms
Optimistic indexingfalse
Contract addresses and start blocks are still hardcoded in src/evm/config.ts. The repo ships with placeholder values and must be updated before real indexing will work.

Prerequisites

Before starting the service, make sure you have:
  • Bun 1.x
  • PostgreSQL
  • an RPC endpoint for the intended Lightchain AI-compatible chain
The project is Bun-first, even though the repository also contains package-lock.json.

Local Setup

1. Install dependencies

CodeBASH

2. Create your environment file

CodeBASH
Current environment variables:
VariableRequiredPurpose
DATABASE_URLYesPostgreSQL connection string
PORTNoExpress port, defaults to 3001
RPC_URLYesEVM RPC endpoint used by the indexer
Example:
CodeBASH

3. Update contract config

Before indexing real chain data, replace the placeholders in src/evm/config.ts:
  • CONFIG.workerRegistry
  • CONFIG.jobRegistry
  • CONFIG.aiConfig
  • CONFIG.workerRegistryStart
  • CONFIG.jobRegistryStart
  • CONFIG.aiConfigStart

4. Generate Checkpoint models

CodeBASH
This generates .checkpoint/models/ from src/schema.gql.

5. Start the service

Development mode:
CodeBASH
Production-style build:
CodeBASH

Package Scripts

CommandWhat it does
bun run codegenRuns checkpoint generate
bun run devRuns codegen, then starts the app in watch mode
bun run buildGenerates models and compiles TypeScript into dist/
bun run startStarts the compiled server from dist/src/index.js

Indexed Event Coverage

WorkerRegistry

  • WorkerRegistered(address,bytes)
  • WorkerDeregistered(address)
  • ModelAdded(address,bytes32)
  • ModelRemoved(address,bytes32)
  • WorkerSlashed(address,uint256,uint256)
  • WorkerJailed(address,uint256)
  • WorkerUnjailed(address)
  • StakeTopUp(address,uint256,uint256)
  • ModelWhitelisted(bytes32)
  • ModelDelisted(bytes32)

JobRegistry

  • SessionCreated(uint256,address,bytes32,address,bytes,bytes)
  • SessionReassigned(uint256,address)
  • JobSubmitted(uint256,uint256,address)
  • JobAcknowledged(uint256,address)
  • JobCompleted(uint256,address,bytes32[],bytes32)
  • JobTimedOut(uint256,address,uint256)
  • JobReleased(uint256,uint256,uint256,uint256)
  • FeeDistributed(uint256,uint256,uint256,uint256)
  • DisputeCreated(uint256,address)
  • DisputeResolved(uint256,bool,uint256)
  • WorkerWithdrawal(address,uint256)

AIConfig

  • ModelRegistered(bytes32,uint256,uint256,uint256)
  • ModelEnabled(bytes32)
  • ModelDisabled(bytes32)

Data Model

The GraphQL schema lives in src/schema.gql. Main entities:
EntityPurpose
WorkerCurrent worker state and counters
WorkerModelWorker-to-model relationship
JobIndexed job lifecycle state
SessionSession-level grouping of jobs
SlashEventSlash records with best-effort correlation
DisputeDispute state for a job
NetworkStatsGlobal counters
ModelInfoPer-model whitelist and config state
Important identifier semantics:
  • Worker.id is the worker address
  • WorkerModel.id is ${workerAddress}/${modelId}
  • Job.id is the stringified on-chain jobId
  • Session.id is the stringified on-chain sessionId
  • NetworkStats.id is always global
  • SlashEvent.id is ${txHash}/${logIndex}

REST API

GET /api/health

Returns:
CodeJSON

POST /api/workers/:address/online

Returns:
CodeJSON
Current behavior:
  • the request body is unused
  • the route does not write to the database
  • it simply calls checkWorkerOnline(address) and returns the result

GraphQL Usage

Checkpoint mounts GraphQL at /. Use src/schema.gql as the source of truth for available entity types, then inspect the live GraphQL schema after startup to see the generated root queries, filters, and pagination arguments. In practice, the GraphQL API is centered on:
  • Worker
  • WorkerModel
  • Job
  • Session
  • SlashEvent
  • Dispute
  • NetworkStats
  • ModelInfo

Derived Behavior To Know About

Some values in the API are derived rather than directly stored on-chain.

Worker status

The code derives worker status from:
  • is_registered
  • is_jailed
  • is_online
Practical interpretation:
StatusCondition
Onlineis_registered && !is_jailed && is_online
Offlineis_registered && !is_jailed && !is_online
Jailedis_registered && is_jailed
Deregistered!is_registered

Slash correlation

The writer layer keeps an in-memory correlation map so a WorkerSlashed record can be linked to:
  • JobTimedOut
  • DisputeResolved(workerGuilty = true)
If that correlation is not available, the extra slash metadata remains null.

Global counters

NetworkStats is updated incrementally by handlers. It is not recomputed from scratch.

Known Caveats

These are current implementation realities:
  1. Online detection is stubbed. src/evm/online-check.ts currently returns true for every worker.
  2. Contract config is not environment-driven. Addresses and start blocks are hardcoded in src/evm/config.ts.
  3. WorkerModel removals are not persisted as removals. handleModelRemoved updates the worker timestamp, but does not delete or deactivate the WorkerModel record.
  4. Some Job fields are placeholders. escrowed_fee and deadline are initialized, but are not populated from indexed events in the current implementation.
  5. Session.status is only partially modeled. Sessions are created as "Active" and are not fully transitioned through every on-chain state.
  6. Slash correlation is in-memory. A process restart can break same-block correlation between related events.
  7. There is no committed test suite yet. Changes should be validated manually or covered with new tests.
The API is useful today, but it should be treated as an evolving indexer rather than a finalized production data contract.

Common Maintenance Tasks

Add a new indexed event

  1. Update the ABI under src/evm/abis if needed.
  2. Add the event mapping in src/evm/config.ts.
  3. Implement the writer in src/evm/writers.ts.
  4. If the schema changes, update src/schema.gql and rerun bun run codegen.

Replace the online check

Implement the real availability logic in src/evm/online-check.ts. Current function contract:
CodeTYPESCRIPT

Move contract config to environment variables

If you want deployable environments without source edits:
  1. Read contract addresses and start blocks from process.env.
  2. Extend .env.example.
  3. Validate those values near the top of src/evm/config.ts.

Troubleshooting

.checkpoint/models is missing

Run:
CodeBASH

The service starts but no data appears

Check these first:
  • RPC_URL points to the intended chain
  • the contract addresses are real
  • the configured start blocks are correct
  • PostgreSQL is reachable through DATABASE_URL

REST works but GraphQL is empty

That usually means Express is healthy but the indexer is not ingesting events. In this repository, the most common causes are:
  • placeholder contract config
  • RPC issues
  • database connectivity issues


Last updated: March 2026