Skip to content

Contributing

Artem Poltorzhitskiy edited this page Mar 24, 2026 · 2 revisions

Contributing

Project Structure

cmd/          # Application entry points (api, indexer, private_api, celestials)
internal/     # Non-exportable packages (storage interfaces, postgres implementations, types)
pkg/          # Importable packages (indexer pipeline, node clients, types)
database/     # SQL scripts, materialized views, PostgreSQL functions
configs/      # YAML configuration with env-var substitution
test/         # Newman API test collection, test fixtures

See Architecture for the full directory breakdown.

Development Workflow

  1. Fork the repository and create a feature branch:

    git checkout -b feature/my-feature   # new feature
    git checkout -b fix/my-bug           # bug fix
    git checkout -b docs/update-readme   # documentation
  2. Make your changes with tests.

  3. Run the full check before committing:

    make gc    # lint β†’ test β†’ git commit

    Or run steps individually:

    make lint
    make test
  4. Submit a pull request with a clear description of what and why.

  5. Address review feedback.

Commit Message Format

We use conventional commits with the following prefixes:

feat: add Hyperlane token indexing
fix: correct block height overflow in ID calculation
docs: update configuration reference
refactor: split storage module into domain files
test: add integration tests for IBC transfers
deps: update celestia-app to v7.0.3
chore: update license headers

Code Standards

Go conventions

  • Run go fmt and golangci-lint before every commit (make lint)
  • Use zerolog for logging β€” never fmt.Print in production paths
  • Use errors.Wrap(err, "context") from github.com/pkg/errors
  • Use storage interfaces only β€” never use concrete postgres.* types outside internal/
  • Use github.com/bytedance/sonic for JSON marshaling (faster than encoding/json)

SPDX license headers

Every new Go source file must begin with:

// SPDX-FileCopyrightText: 2025 Bb Strategy Pte. Ltd. <celenium@baking-bad.org>
// SPDX-License-Identifier: MIT

Apply to all files automatically:

make license-header

Enums

Enum types in internal/storage/types/ are code-generated. Never edit *_enum.go files manually. After changing an enum definition, run:

make generate

Mocks

All storage interface mocks are auto-generated into mock/ subdirectories. Add a //go:generate directive to your interface file, then run:

make generate

Never edit generated mock files manually.

API handlers

Every new handler must include Swagger annotations:

// @Summary     Get block info
// @Tags        block
// @ID          get-block
// @Param       height path integer true "Block height" minimum(1)
// @Produce     json
// @Success     200 {object} responses.Block
// @Failure     400 {object} Error
// @Router      /v1/blocks/{height} [get]
func (h *BlockHandler) Get(c echo.Context) error { ... }

After adding or changing handlers, regenerate the Swagger docs:

make api-docs
# or together with mocks:
make ga

Adding a New Entity (Checklist)

Follow this checklist to add a new indexed entity end-to-end:

  1. Storage model β€” add model struct + filter struct + interface IFoo in internal/storage/
  2. Postgres implementation β€” implement queries in internal/storage/postgres/foo.go using the subquery+JOIN pattern
  3. Register in Storage struct β€” internal/storage/postgres/core.go; add hypertable if time-series
  4. Indexes β€” add DB indexes in internal/storage/postgres/index.go
  5. Transaction save/rollback β€” add save and rollback methods in internal/storage/postgres/transaction.go
  6. Mock β€” add //go:generate directive, run make generate
  7. Migration β€” create a migration file in internal/storage/postgres/migrations/ with both up and down
  8. Parser / DecodeContext β€” add parsing logic; assign deterministic ID via idFromHeightAndPosition(height, position); accumulate into dCtx via ctx.AddXxx() methods
  9. Storage module β€” create pkg/indexer/storage/foo.go with a saveFoo(ctx, tx, dCtx.Foos) function; call it from processBlockInTransaction in storage.go
  10. Rollback β€” add rollback logic in pkg/indexer/rollback/ if needed
  11. API handler β€” create cmd/api/handler/foo.go with Swagger annotations
  12. Response DTO β€” add response struct in cmd/api/handler/responses/
  13. Routes β€” register routes in cmd/api/init.go
  14. Regenerate docs β€” run make ga

Database Migrations

  • Migration files live in internal/storage/postgres/migrations/, named YYYYMMDDNNNNNN_description.go
  • Each file registers exactly one migration via init()
  • Both up and down are required β€” never leave down as a stub or TODO
  • down must fully revert everything up does

Testing Requirements

  • Write unit tests for all new logic
  • Write integration tests for new storage queries β€” these spin up a real TimescaleDB container (Docker required)
  • Use testfixtures for DB test data (fixtures live in test/)
  • Mocks are for handler/service tests only β€” never mock the database in integration tests
  • Ensure make test passes before submitting a PR

Security

  • Validate all external input (user requests, node responses)
  • Never commit secrets, tokens, or credentials
  • Be aware of OWASP Top 10: SQL injection, XSS, command injection, etc.
  • gosec linter is enabled and must pass

Release Process

Releases are tagged from master. The Docker images are published automatically on tag push:

Image Registry
ghcr.io/celenium-io/celestia-indexer Indexer
ghcr.io/celenium-io/celestia-indexer-api Public API
ghcr.io/celenium-io/celestia-indexer-private-api Private API

Clone this wiki locally