Stateless, high-performance Rust REST API for epoch timestamps.
-
GET /now?format=<seconds|ms|ns|iso>[&json]- Returns the current UTC timestamp in the requested format.
- Add
jsonquery key (for example?format=iso&json) to get JSON output.
-
GET /secnow?format=<seconds|ms|ns|iso>- Returns JSON by default:
{ "format": "iso", "timestamp": "2026-01-01T00:00:00+00:00", "signature": "<base64url_signature>", "token": "iso:2026-01-01T00:00:00+00:00.<base64url_signature>" }
- Returns JSON by default:
-
POST /validate- Accepts exactly the JSON returned by
/secnow. - Returns
200only when:tokensignature is valid,signaturematches the token suffix,- and
format/timestampstill match the token payload.
- Returns
400for tampered/invalid payloads.
- Accepts exactly the JSON returned by
The API signs timestamps using Ed25519:
- Environment variable:
ED25519_PRIVATE_KEY_HEX - Must be a 32-byte private key represented as hex.
- The service fails to start if this variable is missing.
cargo run --releasedocker build -t epochapi .
docker run --rm -p 8080:8080 \
-e ED25519_PRIVATE_KEY_HEX=<64_hex_chars> \
epochapicargo testTo smoke-test the distroless container image locally (requires Docker, curl, and jq):
./scripts/docker-smoke-test.shThe smoke test injects ED25519_PRIVATE_KEY_HEX automatically by generating an ephemeral key unless you explicitly provide one in your environment.
OpenAPI 3.0 spec is available at:
openapi.yaml
This repository treats openapi.yaml as the canonical API contract.
- Web documentation and API tester are generated from the spec.
- Rust contract interface (
api_contract::EpochApiContract) is generated from the spec. - Backend implementation compiles against that generated trait.
Generate artifacts:
./scripts/generate-all.shCheck generated artifacts are committed and up-to-date:
./scripts/check-generated-clean.shA separate Astro site lives in web/.
Install dependencies and build:
cd web
npm install
npm run build
npm run test:unit
npx playwright install chromium
npm run test:a11yRun locally:
cd web
PUBLIC_API_BASE_URL=http://localhost:8080 npm run devRuntime conformance uses uv to create a local virtual environment, install Schemathesis, and run contract checks:
./scripts/contract-test.shThe compose setup mirrors a production split-domain topology:
example.com-> docs site (static Astro build)api.example.com-> API serviceedge(nginx) acts as the local reverse proxy / ingress
Start the full stack:
docker compose up --buildTo route example.com and api.example.com to your local machine, add these host entries:
127.0.0.1 example.com
127.0.0.1 api.example.com
Then visit:
http://example.comfor docshttp://api.example.com/now?format=isofor API
To keep local debugging convenient, compose also exposes direct ports:
http://localhost:3000-> docs container directlyhttp://localhost:8080-> API container directly
These direct ports are a local-only convenience and are not intended as production exposure.
web/Dockerfile builds the Astro static site and serves it from a distroless Node runtime.
This keeps the docs runtime image minimal while still allowing static hosting behavior similar
to production.