Skip to content

Commit 3a8ac34

Browse files
committed
Align docs with current platform behavior
Update the written guides and generated OpenAPI metadata to match the current repo layout, env-loading workflow, and API contract. This also fixes the health/OpenAPI smoke test and documents the remaining legacy Agora compatibility behavior more precisely.
1 parent 9a88258 commit 3a8ac34

13 files changed

Lines changed: 497 additions & 429 deletions

File tree

AGENTS.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
# Repository Guidelines
22

33
## Project Structure & Module Organization
4-
The workspace is a multi-crate Cargo project. Service code lives under `api/` (Axum REST server with Swagger UI) and `indexers/{indexer-1,indexer-2,indexer-3}/` for ingestion services. Shared libraries (`shared/common`, `shared/db-core`, `shared/entities`) hold reusable logic and schema definitions. Database migrations reside in `migration/`, while service-specific settings inherit from the root `config.toml` alongside `api/config.toml` and `indexers/<name>/config.toml`.
4+
The workspace is a multi-crate Cargo project. Service code lives under `api/` (Axum REST server with Swagger UI) and `indexers/{discourse-indexer,near-indexer,telegram-indexer}/` for ingestion services. Shared libraries (`shared/common`, `shared/db-core`, `shared/entities`) hold reusable logic and schema definitions. Database migrations reside in `migration/`, while service-specific defaults live in `api/config.toml` and `indexers/<name>/config.toml`.
55

66
## Build, Test, and Development Commands
7-
Use `cargo check-all` to compile every crate before opening a PR. Run `cargo test-all` for the full workspace test suite; scope to a crate with commands such as `cargo test -p api` or `cargo test -p api handlers::health`. Development binaries include `cargo run-api` (serves at http://127.0.0.1:3000) and `cargo run-indexer-1` (similar for other indexers). Apply schema updates with `cargo migrate-up`, generate new migrations via `cargo migrate-generate <name>`, and rebuild SeaORM entities with `cargo generate-entities`.
7+
Use `cargo check-all` to compile every crate before opening a PR. Run `cargo test-all` for the full workspace test suite; scope to a crate with commands such as `cargo test -p api` or `cargo test -p api health_`. Development binaries include `cargo run-api` (serves at http://127.0.0.1:3000) and `cargo run-indexer-1` (similar for other indexers). Apply schema updates with `cargo migrate-up`, generate new migrations via `cargo migrate-generate <name>`, and rebuild SeaORM entities with `cargo generate-entities`.
88

99
## Coding Style & Naming Conventions
1010
Target Rust 2021 idioms with 4-space indentation, `snake_case` for modules/functions, and `CamelCase` types. Libraries favor `thiserror` for typed errors; binaries use `anyhow` for rich context. Always format with `cargo fmt` and consider `cargo clippy -- -D warnings` before pushing.
1111

1212
## Testing Guidelines
13-
Tests mirror module layout (e.g., `api/tests/handlers/`). Prefer deterministic handler tests for business logic and route tests for HTTP behavior. Frameworks in use include `rstest`, `axum-test`, and `testcontainers` for PostgreSQL-backed scenarios. Run targeted suites during development, then confirm with `cargo test-all` prior to merge.
13+
Tests mirror the service surface (for example, `api/tests/{unit,e2e}/`). Prefer deterministic handler tests for business logic and route tests for HTTP behavior. Frameworks in use include `rstest`, `axum-test`, and `testcontainers` for PostgreSQL-backed scenarios. Run targeted suites during development, then confirm with `cargo test-all` prior to merge.
1414

1515
## Commit & Pull Request Guidelines
1616
Write concise, imperative commit subjects (≈50 characters) similar to "fix Railway caching" or "observability". Pull requests should summarize the change, link relevant issues, note testing performed, and call out config or migration impacts; include Swagger screenshots when UI behavior changes.
1717

1818
## Security & Configuration Tips
19-
Never commit secrets. Copy `.env.example` to `.env` locally and configure runtime with environment variables such as `DATABASE_URL` and `RUST_LOG`. Override Figment settings by exporting keys like `APP_SERVER__HOST` or `APP_LOGGING__LEVEL` for service-specific tuning.
19+
Never commit secrets. Copy `.env.example` to `.env.local` locally and configure runtime with environment variables such as `DATABASE_URL` and `APP_LOGGING__LEVEL`. If you use the checked-in `.envrc`, install the Doppler CLI because the direnv workflow invokes it before sourcing `.env.local`. Override Figment settings by exporting keys like `APP_SERVER__HOST` or `APP_LOGGING__LEVEL` for service-specific tuning, and use `RUST_LOG` only when you need a fine-grained tracing filter override.
2020

2121
## Documentation Maintenance
2222
Keep the files in `docs/` in sync with the codebase. Update them as part of the same PR that introduces the change:

README.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,21 @@ hos-api/
2626
```bash
2727
git clone <repository-url> && cd hos-api
2828
cargo install sea-orm-cli --version 1.1.15 --locked
29-
cp .env.example .env # Edit with database settings
30-
direnv allow
29+
cp .env.example .env.local # Used by the checked-in .envrc
30+
direnv allow # Requires Doppler CLI; loads env for DB-backed migration/entity commands
3131
cargo migrate-up && cargo generate-entities
3232
cargo run-api
3333
```
3434

3535
**Access**: API at http://localhost:3000[Swagger UI](http://localhost:3000/swagger-ui)[OpenAPI spec](http://localhost:3000/api-docs/openapi.json)
3636

37+
If you do not use the checked-in `direnv` + Doppler workflow, export
38+
`DATABASE_URL` manually before running DB-backed commands such as
39+
`cargo migrate-up`, `cargo migrate-down`, `cargo migrate-status`,
40+
`cargo migrate-reset`, `cargo migrate-fresh`, `cargo generate-entities`, or
41+
`cargo backfill-identity-accounts`. `cargo migrate-generate` does not require a
42+
live database connection.
43+
3744
**Detailed setup**: [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md)
3845

3946
## Database Operations
@@ -68,7 +75,7 @@ This monorepo ships as independently deployable services plus a shared PostgreSQ
6875
| Component | Role | Public ingress | Main requirements |
6976
|---|---|---|---|
7077
| `api` | Axum REST API and Swagger UI | Yes | `DATABASE_URL`, platform `PORT` or `APP_SERVER__PORT` |
71-
| `web` | Next.js frontend | Yes | `VENEAR_API_BASE_URL` |
78+
| `web` | Next.js frontend | Yes | `VENEAR_API_BASE_URL` or `NEXT_PUBLIC_VENEAR_API_BASE_URL` |
7279
| `discourse-indexer` | Discourse ingestion worker | No | `DATABASE_URL` |
7380
| `near-indexer` | NEAR ingestion worker | No | `DATABASE_URL`, optional FastNEAR credentials |
7481
| `telegram-indexer` | Telegram ingestion worker | No | `DATABASE_URL`, Telegram credentials, durable session state |
@@ -299,13 +306,13 @@ Notes for this compose stack:
299306
- `api`: `/api/Dockerfile` and `/api/railway.toml`
300307
- `web`: `/web/Dockerfile` and `/web/railway.toml`
301308
- `migration`: `/migration/Dockerfile`
302-
- `discourse-indexer`: `/indexers/discourse-indexer/Dockerfile` and `/indexers/discourse-indexer/railway.toml`
303-
- `near-indexer`: `/indexers/near-indexer/Dockerfile` and `/indexers/near-indexer/railway.toml`
304-
- `telegram-indexer`: `/indexers/telegram-indexer/Dockerfile` and `/indexers/telegram-indexer/railway.toml`
309+
- `discourse-indexer`: `/indexers/discourse-indexer/Dockerfile`
310+
- `near-indexer`: `/indexers/near-indexer/Dockerfile`
311+
- `telegram-indexer`: `/indexers/telegram-indexer/Dockerfile`
305312

306313
This layout works on Railway, Fly.io, Render, Kubernetes, ECS, Nomad, or plain Docker Compose as long as the platform can run one container per service, inject environment variables, provide PostgreSQL connectivity, and expose public HTTP only for `api` and `web`.
307314

308-
Railway is the easiest fit today because this repo already includes one `railway.toml` per service, Dockerfile-based builds, watch patterns, and API support for Railway's injected `PORT` environment variable. If you use Railway, prefer one service per component, run migrations as a separate release step, and use private networking from `web` to `api` with a base URL like `http://${{api.RAILWAY_PRIVATE_DOMAIN}}:${{api.PORT}}/api/v1/venear`.
315+
Railway is the easiest fit today because this repo already includes Railway manifests for the long-running services, Dockerfile-based builds for every deployable component, watch patterns, and API support for Railway's injected `PORT` environment variable. If you use Railway, prefer one service per component, run migrations as a separate release step, and use private networking from `web` to `api` with a base URL like `http://${{api.RAILWAY_PRIVATE_DOMAIN}}:${{api.PORT}}/api/v1/venear`.
309316

310317
## Testing
311318

@@ -325,7 +332,7 @@ The platform uses `tracing` with `tracing-subscriber` and emits structured JSON
325332
**Features**:
326333
- **Logs**: Structured JSON logs for all services
327334
- **Traces**: Span-aware request and background-job tracing
328-
- **Filtering**: Log level control via `RUST_LOG`
335+
- **Filtering**: Log level control via `APP_LOGGING__LEVEL`
329336

330337
## Tech Stack
331338

api/src/docs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use utoipa::OpenApi;
88
info(
99
title = "HOS API",
1010
version = "0.1.0",
11-
description = "Read-only REST API for indexed identity, Discourse, Telegram, and veNEAR governance data.\n\nOpenAPI contract notes for AI clients:\n- The API is read-only. Every endpoint is discoverable through generated OpenAPI metadata.\n- List endpoints are consistently paginated with `limit` and `cursor`.\n- Cursor pagination uses opaque base64url cursors.\n - `limit` defaults to 20 and is clamped to a maximum of 100.\n - `cursor` is an opaque base64url token from a prior response and should be treated as an opaque string.\n - Omit `cursor` to request the first page in a sequence.\n - Continue until `next_cursor` is `null` or absent.\n- All paginated responses return `data`, `next_cursor`, and `has_more`.\n- `next_cursor` and pagination order are endpoint-specific; always follow the ordering declared in each endpoint description.\n- Timestamp fields use RFC 3339 / ISO-8601 format with timezone.\n- Error responses follow RFC 9457 Problem Details as `application/problem+json`, including machine-friendly `code` when present.\n- Indexers are asynchronous: newly emitted data may lag or reorder briefly during backfill.\n- `identity` values are stable while indexer-sourced entity references may vary by environment and snapshot time.",
11+
description = "Read-only REST API for indexed identity, Discourse, Telegram, and veNEAR governance data.\n\nOpenAPI contract notes for AI clients:\n- The API is read-only. Every endpoint is discoverable through generated OpenAPI metadata.\n- `/api/v1` list endpoints use `limit` and `cursor` with opaque base64url cursors.\n - `limit` defaults to 20 and is clamped to a maximum of 100.\n - `cursor` is an opaque base64url token from a prior response and should be treated as an opaque string.\n - Omit `cursor` to request the first page in a sequence.\n - Continue until `next_cursor` is `null` or absent.\n- Paginated `/api/v1` responses return `data`, `next_cursor`, and `has_more`.\n- Legacy `/api/agora` endpoints intentionally preserve page-based params and older payload shapes for compatibility.\n- `next_cursor` and pagination order are endpoint-specific; always follow the ordering declared in each endpoint description.\n- Timestamp fields use RFC 3339 / ISO-8601 format with timezone.\n- Error responses follow RFC 9457 Problem Details as `application/problem+json`, including machine-friendly `code` when present.\n- Indexers are asynchronous: newly emitted data may lag or reorder briefly during backfill.\n- `identity` values are stable while indexer-sourced entity references may vary by environment and snapshot time.",
1212
contact(
1313
name = "HOS API Team",
1414
url = "https://github.com/HackHumanityOrg/hos-api"

api/src/handlers/health/health_check.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ use tracing::{info, instrument};
1515
description = "Performs service liveness checks and an optional database probe (`SELECT 1`). Returns `healthy` when the database probe succeeds and `degraded` when the database is unavailable or fails.",
1616
tag = "health",
1717
responses(
18-
(status = 200, description = "Current API health report", body = HealthResponse),
19-
(status = 500, description = "Health check failed", body = crate::errors::ApiError)
18+
(status = 200, description = "Current API health report", body = HealthResponse)
2019
)
2120
)]
2221
#[instrument(skip(app_state))]

api/src/handlers/venear/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! Canonical-query veNEAR governance handlers.
2+
#![allow(clippy::result_large_err)]
23

34
pub mod accounts;
45
pub mod activity;

api/src/handlers/venear/sql.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub(crate) struct ParsedEventLog {
1616
pub log_index: i64,
1717
}
1818

19+
#[allow(dead_code)]
1920
#[derive(Debug, Clone)]
2021
pub(crate) struct CanonicalAction {
2122
pub id: String,
@@ -127,6 +128,7 @@ pub(crate) fn decode_row_col<T: sea_orm::TryGetable>(
127128
})
128129
}
129130

131+
#[allow(dead_code)]
130132
pub(crate) async fn scalar_i64_with_values(
131133
db: &DatabaseConnection,
132134
sql: &str,

api/tests/e2e/openapi.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ fn openapi_spec_exposes_health_path_and_schemas() {
1515
.and_then(|v| v.as_str())
1616
.expect("api description present");
1717
assert!(
18-
description.contains("Cursor pagination uses opaque base64url cursors"),
19-
"api description should document pagination conventions"
18+
description.contains("/api/v1` list endpoints use `limit` and `cursor`")
19+
&& description
20+
.contains("Legacy `/api/agora` endpoints intentionally preserve page-based params"),
21+
"api description should document both canonical and Agora pagination conventions"
2022
);
2123

2224
let paths = value

0 commit comments

Comments
 (0)