Devin 791184be34 docs(swagger)+test(rest): document /auth/refresh + /auth/logout, add HTTP smoke tests
Follow-up to PR #8 (JWT revocation + refresh), addressing the two
in-scope follow-ups called out in the completion-sequence summary on
PR #11:

  1. swagger.yaml pre-dated /api/v1/auth/refresh and /api/v1/auth/logout
     - client generators could not pick them up.
  2. Those handlers were covered by unit tests on the WalletAuth layer
     and by the e2e-full Playwright spec, but had no HTTP-level unit
     tests - regressions at the mux/handler seam (wrong method,
     missing walletAuth, unregistered route) were invisible to
     go test ./backend/api/rest.

Changes:

backend/api/rest/swagger.yaml:
  - New POST /api/v1/auth/refresh entry under the Auth tag.
    Uses bearerAuth, returns the existing WalletAuthResponse on 200,
    401 via components/responses/Unauthorized, 503 when the auth
    storage or the jwt_revocations table from migration 0016 is
    missing. Description calls out that legacy tokens without a jti
    cannot be refreshed.
  - New POST /api/v1/auth/logout entry. Same auth requirement;
    returns {status: ok} on 200; 401 via Unauthorized; 503 when
    migration 0016 has not run. Description names the jwt_revocations
    table explicitly so ops can correlate 503s with the migration.
  - Both slot in alphabetically between /auth/wallet and /auth/register
    so the tag block stays ordered.

backend/api/rest/auth_refresh_internal_test.go (new, 8 tests):
  - TestHandleAuthRefreshRejectsGet - GET returns 405 method_not_allowed.
  - TestHandleAuthRefreshReturns503WhenWalletAuthUnconfigured -
    walletAuth nil, POST with a Bearer header returns 503 rather
    than panicking (guards against a regression where someone calls
    s.walletAuth.RefreshJWT without the nil-check).
  - TestHandleAuthLogoutRejectsGet   - symmetric 405 on GET.
  - TestHandleAuthLogoutReturns503WhenWalletAuthUnconfigured -
    symmetric 503 on nil walletAuth.
  - TestAuthRefreshRouteRegistered - exercises SetupRoutes and
    confirms POST /api/v1/auth/refresh and /api/v1/auth/logout are
    registered (i.e. not 404). Catches regressions where a future
    refactor drops the mux.HandleFunc entries for either endpoint.
  - TestAuthRefreshRequiresBearerToken +
    TestAuthLogoutRequiresBearerToken - sanity-check that a POST
    with no Authorization header resolves to 401 or 503 (never 200
    or 500).
  - decodeErrorBody helper extracts ErrorDetail from writeError's
    {"error":{"code":...,"message":...}} envelope, so asserts
    on body["code"] match the actual wire format (not the looser
    {"error":"..."} shape).
  - newServerNoWalletAuth builds a rest.Server with JWT_SECRET set
    to a 32-byte string of 'a' so NewServer's fail-fast check from
    PR #3 is happy; nil db pool is fine because the tests do not
    exercise any DB path.

Verification:
  cd backend && go vet ./...             clean
  cd backend && go test ./api/rest/      pass (17 tests; 7 new)
  cd backend && go test ./...            pass

Out of scope: the live credential rotation in the third follow-up
bullet requires infra access (database + SSH + deploy pipeline) and
belongs to the operator.
2026-04-18 19:41:21 +00:00

SolaceScan Explorer

Multi-tier block explorer and access-control plane for Chain 138.

Four access tiers:

Track Who Auth Examples
1 Public None /blocks, /transactions, /search
2 Wallet-verified SIWE JWT RPC API keys, subscriptions, usage reports
3 Analytics SIWE JWT (admin or billed) Advanced analytics, audit logs
4 Operator SIWE JWT (operator.*) run-script, mission-control, ops

See docs/ARCHITECTURE.md for diagrams of how the tracks, services, and data stores fit together, and docs/API.md for the endpoint reference generated from backend/api/rest/swagger.yaml.

Repository layout

backend/           Go 1.23 services (api/rest, indexer, auth, analytics, ...)
frontend/          Next.js 14 pages-router app
deployment/        docker-compose and deploy manifests
scripts/           e2e specs, smoke scripts, operator runbooks
docs/              Architecture, API, testing, security, runbook

Quickstart (local)

Prereqs: Docker (+ compose), Go 1.23.x, Node 20.

# 1. Infra deps
docker compose -f deployment/docker-compose.yml up -d postgres elasticsearch redis

# 2. DB schema
cd backend && go run database/migrations/migrate.go && cd ..

# 3. Backend (port 8080)
export JWT_SECRET=$(openssl rand -hex 32)
export CSP_HEADER="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' http://localhost:8080 ws://localhost:8080"
cd backend/api/rest && go run . &

# 4. Frontend (port 3000)
cd frontend && npm ci && npm run dev

Or let make e2e-full do everything end-to-end and run Playwright against the stack (see docs/TESTING.md).

Configuration

Every credential, URL, and RPC endpoint is an env var. There is no in-repo production config. Minimum required by a non-dev binary:

Var Purpose Notes
JWT_SECRET HS256 wallet-auth signing key Fail-fast if empty
CSP_HEADER Content-Security-Policy response header Fail-fast if empty
DB_HOST / DB_PORT / DB_USER / DB_PASSWORD / DB_NAME Postgres connection
REDIS_HOST / REDIS_PORT Redis cache
ELASTICSEARCH_URL Indexer / search backend
RPC_URL / WS_URL Upstream Chain 138 RPC
RPC_PRODUCTS_PATH Optional override for backend/config/rpc_products.yaml PR #7

Full list: deployment/ENVIRONMENT_TEMPLATE.env.

Testing

# Unit tests + static checks
cd backend && go test ./... && staticcheck ./... && govulncheck ./...
cd frontend && npm test && npm run test:unit

# Production canary
EXPLORER_URL=https://explorer.d-bis.org make test-e2e

# Full local stack + Playwright
make e2e-full

See docs/TESTING.md.

Contributing

Branching, PR template, CI gates, secret handling: see CONTRIBUTING.md. Never commit real credentials — .gitleaks.toml will block the push and rotation steps live in docs/SECURITY.md.

Licence

MIT.

Description
No description provided
Readme 50 MiB
Languages
Shell 32.1%
TypeScript 26.6%
Go 20%
JavaScript 16.4%
HTML 4.4%
Other 0.4%