skills/sails-indexer/SKILL.md
Use when a builder needs a read-side indexer and query API for a standard Gear/Vara Sails app using program events, IDL-driven decoding, projected read models, and optional on-chain query enrichment. Do not use for command-side backends, generic Node APIs, non-Sails repositories, Vara.eth or ethexe-first work.
npx skillsauth add gear-foundation/vara-skills sails-indexerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Design and implement the read-side backend for a standard Gear/Vara Sails app.
Use this skill when the frontend or integration layer needs history, pagination, filtering, search, aggregates, charts, timelines, or a fast read API that should not be rebuilt from direct chain queries on every screen load.
Treat the indexer as a projection pipeline:
chain/archive -> Sails event decode -> optional on-chain enrichment -> Postgres read model -> thin GraphQL API
../../references/gear-execution-model.md../../references/gear-messaging-and-replies.md../../references/sails-program-and-service-architecture.md../../references/sails-idl-client-pipeline.md../../references/sails-cheatsheet.md../../references/sails-gtest-and-local-validation.md../../references/scale-binary-decoding-guide.md../../references/contract-interface-evolution.md../../references/sails-indexer-patterns.md../../references/sails-idl-v2-syntax.md — IDL v2 event syntax (throws, @query, service-scoped types).idl as the event and route contract for normal Sails decoding work./graphql and make it reachable from local frontend development without browser CORS failures.postgraphile(...), PostGraphileOptions, appendPlugins), so keep postgraphile on the 4.x line and postgraphile-plugin-connection-filter on the 2.x line when copying these assets. Do not switch to v5 by changing package versions alone; update the API bootstrap and template config together.../../references/sails-indexer-patterns.md, especially Start with configuration, not hardcoded endpoints, Build one shared batch processor and export its derived types, Centralize all IDL decoding behind one decoder, Wire the runtime in main.ts, but do not place projection logic there, Give every handler the same lifecycle contract, Process a batch in a stable order, Save in groups and only write what changed, and Expose a thin API layer on top of PostgreSQL..idl and mapping those events into off-chain entitiesBefore implementation, make these inputs explicit:
.idl files or generated decode artifacts for each indexed programfromBlock or backfill start ruleIf any of these are unknown, stop and write the missing assumptions explicitly before coding.
Cross-check against ../../references/sails-indexer-patterns.md:
Start with configuration, not hardcoded endpoints, Build one shared batch processor and export its derived types, and Create a typed boundary for Gear events before touching business payloads for config, processor boundary, and typed Gear event boundaryCentralize all IDL decoding behind one decoder for decoder setup and query encoding or decodingKeep the SQL schema read-model oriented before freezing the SQL or ORM schemaUse this cold-start path when the repo does not already contain a working indexer scaffold.
Templates live in skills/sails-indexer/assets/:
package.jsontsconfig.jsondocker-compose.yml.env.exampleCold-start defaults:
reflect-metadata imported once at the top of each runtime entrypoint before TypeORM entities or data source code is loadedexperimentalDecorators: true and emitDecoratorMetadata: true enabled in tsconfig.jsonVARA_ARCHIVE_URL) and Vara archive RPC URL (VARA_RPC_URL); Subsquid performs historical queries that require an archive node on the RPC side, so do not point VARA_RPC_URL at a live non-archive endpointSuggested first-run order:
assets/ into the new indexer repo..env from .env.example.npm install.docker compose up -d if using Docker).schema.graphql with npm run codegen.npm run build.npm run db:generate.npm run db:apply.npm run dev:processor.npm run dev:api./graphql is reachable through the chosen local access path.For schema updates, use this order:
schema.graphqlnpm run codegennpm run buildnpm run db:generatenpm run db:applyFor fixed-program projects, .env can stay minimal with one VARA_PROGRAM_ID and one VARA_IDL_PATH.
For discovery-driven projects, replace that pair with the explicit root IDs that serve as discovery sources — REGISTRY_PROGRAM_ID, FACTORY_PROGRAM_ID, or other domain-specific anchors — and keep discovery logic separate from projection logic. If the set of programs is small and known upfront, list them directly in config rather than building a discovery pipeline.
.idl files: service, event name, payload shape, and source program.Map this sequence to the reference before writing code:
Decide Whether The Project Needs An Indexer FirstSplit dynamic discovery from domain projectionCreate a typed boundary for Gear events before touching business payloads, Keep chain event guards in small helpers, Centralize all IDL decoding behind one decoder, and Decode only after confirming that the message is a Sails eventKeep the SQL schema read-model oriented and Expose a thin API layer on top of PostgreSQLConvert events into append-only activity records with deterministic IDs and Deterministic IDsSource-Of-Truth RulesSplit dynamic discovery from domain projection and Keep explicit runtime state per tracked programRehydrate tracked programs from the database on first processing run, Process a batch in a stable order, Save in groups and only write what changed, and Idempotency And Restart RulesExpose a thin API layer on top of PostgreSQLExpose a thin API layer on top of PostgreSQLMinimal Build OrderFor each projected entity, state which source answers which question:
Do not mix these roles implicitly.
Reference: ../../references/sails-indexer-patterns.md section Source-Of-Truth Rules.
If one program emits “child created”, “resource registered”, “room opened”, “position created”, or similar discovery events, isolate that logic from the handler that projects activity inside the created child program.
Preferred split:
Reference: ../../references/sails-indexer-patterns.md sections Split dynamic discovery from domain projection, Keep explicit runtime state per tracked program, and Rehydrate tracked programs from the database on first processing run.
The read model should answer frontend or integration questions efficiently.
Common projected entity types:
Do not copy every on-chain field into SQL unless a consumer actually needs it.
Reference: ../../references/sails-indexer-patterns.md sections Keep the SQL schema read-model oriented and Expose a thin API layer on top of PostgreSQL.
A valid design must explain:
Reference: ../../references/sails-indexer-patterns.md section Idempotency And Restart Rules.
The API should expose projected data cleanly, but the API layer should not become the place where chain events are reinterpreted from scratch.
Reference: ../../references/sails-indexer-patterns.md section Expose a thin API layer on top of PostgreSQL.
A standard implementation usually needs these areas:
src/config.ts for endpoints, IDs, and environment contractsrc/processor.ts for archive or chain batch ingestionsrc/decoder/ or src/sails-decoder.ts for IDL-driven decode helperssrc/types/ and src/helpers/ for typed Gear boundary and event guardssrc/handlers/ for discovery and domain projection handlerssrc/services/ for enrichment and aggregate calculationssrc/model/ for entities and persistence mappingsrc/api/ or src/api.ts for thin GraphQL API exposuresrc/api/graphql.ts or equivalent GraphQL bootstrap module with an explicit /graphql mountschema.graphql or ORM schema plus DB migrationsReference path for this layout:
Start with configuration, not hardcoded endpoints through Give every handler the same lifecycle contract for bootstrap and lifecycleSplit dynamic discovery from domain projection through Save in groups and only write what changed for discovery, runtime state, projection, and persistenceKeep the SQL schema read-model oriented for schema shapeExpose a thin API layer on top of PostgreSQL for API exposureStandard Project Layout for the final filesystem viewName any deviation from this shape explicitly.
The work product must make these decisions explicit:
When producing implementation plans or code, mirror the order from ../../references/sails-indexer-patterns.md rather than jumping directly into handlers or schema work.
Before handoff, require at least:
fromBlock/graphql is mounted and serves the projected read modelReference checkpoints:
Rehydrate tracked programs from the database on first processing run for restart rehydrationProcess a batch in a stable order for stable batch orderBuild rolling metrics from persisted and pending snapshots together for rolling metrics over persisted plus pending stateSave in groups and only write what changed for grouped persistenceIdempotency And Restart Rules for replay safety.idl for normal Sails decoding work unless the task is intentionally about raw SCALE, metadata, or route-debugging./graphql endpoint is mounted and reachable.../sails-program-evolution/SKILL.md before freezing the projection model.../../references/sails-indexer-patterns.md, especially the sections "Wire the runtime in main.ts, but do not place projection logic there", "Give every handler the same lifecycle contract", "Split dynamic discovery from domain projection", "Process a batch in a stable order", and "Save in groups and only write what changed".development
Use when approved tasks require Rust code changes in a Sails ethexe workspace with the ethexe feature enabled. Do not use for standard Gear/Vara Sails apps without ethexe, or when the spec or architecture is still unsettled.
development
Use when a builder needs to design or review architecture for a Sails ethexe app with dual-transport, payable methods, Solidity interface generation, or Ethereum-style events. Do not use for standard Gear/Vara Sails apps without ethexe.
development
Use when a builder needs to design or debug calls from a standard Gear/Vara Sails program into runtime builtin actors such as BLS12-381, staking, proxy, or ETH bridge, including ActorId derivation, request encoding, reply decoding, and gas or ED budgeting. Do not use for regular program-to-program messaging, Vara.eth or ethexe-only work, non-Sails repositories, or runtime-maintenance tasks inside the Gear repo.
development
Use when an agent needs to interact with Vara Network on-chain — deploy programs, call Sails methods, manage wallets, transfer tokens, monitor events. Do not use for building Sails programs.