Skip to content

Stellar Index v0.32.0

Choose a tag to compare

@github-actions github-actions released this 11 Aug 19:16
· 180 commits to main since this release

[v0.32.0] — 2026-08-11

  • Projector decodes each lake event exactly once (stake-buffer
    investigation 2026-08-11): the append-log lake is read without FINAL,
    so re-ingested duplicate rows reached every decoder — absorbed by
    keyed sinks for stateless sources, but CORRUPTING for buffered
    decoders (phoenix's multi-event correlation: a duplicate re-opens a
    completed group and can cross-assign fields between a mixed op's
    legs — the 616 bond/unbond class). An adjacent-identity guard at the
    single decode entry point (mirroring the completeness reconcile's
    guard) now skips exact re-deliveries; events_emitted stops
    over-counting duplicates.

Added

  • Passkey (WebAuthn) sign-in for the dashboard: six new
    endpoints under /v1/auth/passkey/begin-login /
    finish-login (anonymous, usernameless discoverable-credential
    flow; finish mints the SAME session cookie the email-code flow
    does, via the shared session-mint path), begin-register /
    finish-register (session-gated; resident key required so the
    credential can sign in usernameless), and credentials
    (GET list + DELETE {id}, session-gated, owner-scoped). Server
    is github.com/go-webauthn/webauthn v0.17.4; RP ID/origin derive
    from the existing api.dashboard.base_url. Ceremony state rides
    a 5-minute HMAC-signed HttpOnly cookie (purpose-bound so a
    registration challenge can't finish a login); a sign-count
    regression (possible cloned authenticator) refuses the login and
    logs. Storage is the new webauthn_credentials table (migration
    0140, additive). Explorer: "Sign in with a passkey" on /signin
    (feature-detected) + a Passkeys list/add/remove card on
    /dashboard/settings. OpenAPI paths + all three generated
    artifacts refreshed; SDK triage recorded in
    uncoveredOperations.

Security

  • Dashboard 6-digit sign-in codes are no longer derivable from
    the database
    (parked audit finding, aggregate+dashboardauth
    cold audit 2026-08-03): the code was an unkeyed public function
    of magic_link_tokens.token_hash (base32 of its first 4 bytes),
    so any Postgres read — SQL injection elsewhere, a stolen backup —
    yielded every in-flight sign-in code directly, no brute force
    needed, and with it a session for any address the reader could
    trigger a login for. The code is now
    HMAC-SHA256(server_secret, token_hash) reduced to 6 digits —
    same UX, same storage, one derivation swapped; the secret lives
    in config/env (api.dashboard.code_secret_env, default
    STELLARINDEX_DASHBOARD_CODE_SECRET), never in Postgres. With
    the env unset the API falls back to a random per-process secret
    (still keyed; in-flight codes just don't survive a restart —
    they live 15 minutes and the magic link is unaffected). Deploy
    note: codes emailed before the deploy stop verifying for their
    remaining TTL; links keep working.
    onboarding path**: one unauthenticated POST (empty body fine;
    optional name + contact-only email, never verified) creates a
    free-tier platform account and mints its first Postgres-backed API
    key, returning {account_id, api_key, key_id, key_prefix, tier, limits} with the plaintext shown once. Rides the same per-IP
    signup throttle as /v1/signup (shared budget → 429) and the
    signup Content-Type CSRF gate. OpenAPI path + all three generated
    artifacts refreshed (docs-api, docs-postman, web-generate-api);
    SDK triage recorded in uncoveredOperations; agent-facing
    walkthrough at docs/agent-onboarding.md.

Changed

  • Tier model collapsed to anon / free / partner (follow-up
    to the Stripe removal — the platform is free). free is every
    registered account's default, anchored to the old Starter numbers
    (1000 req/min, 1M req/month, 25 keys, 10 webhooks, 25 price
    alerts); partner is staff-set per-account limits via the existing
    PATCH /v1/admin/accounts/{id} override + key-clamp path, with the
    old Enterprise numbers as ceilings when no override is set
    (100k req/min, 1B req/month, 250 keys, 100 webhooks, 1000 alerts);
    anon documents the unauthenticated 60/min per-IP baseline.
    Legacy stored tier strings map in code
    (platform.Tier.Canonical: starter→free,
    pro/business/enterprise→partner; unknown fails closed to free) and
    writes fold back to CHECK-legal strings
    (platform.Tier.StorageValue) — migrations untouched. The admin
    PATCH accepts both vocabularies and canonicalises.

Removed

  • Stripe/billing integration removed — the platform is free
    (operator decision 2026-08-10: anonymous access, free accounts,
    staff-set partner limits; no payments). Deleted the
    POST /v1/webhooks/stripe endpoint (handler + route + OpenAPI path
    • generated artifacts), [api.stripe] config
      (STELLARINDEX_STRIPE_WEBHOOK_SECRET), platform.BillingStore /
      Subscription / StripeEvent and their Postgres store,
      Account.StripeCustomerID + GetByStripeCustomerID, the
      stellarindex_stripe_platform_sync_errors_total +
      stellarindex_stripe_dead_letters_open metrics with both alert-rule
      trees and their runbooks, and paid-plan copy in the explorer
      (pricing/signup/company/dashboard now describe free access).
      The shared tier-clamp machinery the admin
      PATCH /v1/admin/accounts/{id} path uses survives in
      internal/api/v1/keybudgets.go (StripeKeyManager
      SelfServiceKeyManager). Migrations are untouched — historical
      stripe_* columns/tables stay in place, unused.

Fixed

  • Contract WASM view resolves pre-capture contracts ("this
    contract's on-chain WASM isn't in the captured ledger window yet",
    operator report 2026-08-11): the instance→hash hop now reads the
    genesis-complete contract_instance_changes index first, so any
    contract whose instance was ever written resolves its current
    executable (or SAC verdict) regardless of the live-capture window;
    the code-bytes hop was already lake-complete (r1-measured: all
    4,534 contract_code keys present). Legacy read remains the fallback.
  • Trade USD valuation: divergent-leg cross-check (fake-XMR
    incident 2026-08-11): an attacker planted an INDUSX/XLM bridge rate
    for the cost of the $0.01 dust floor and two no-XLM-leg trades were
    stamped ~$91M each off the poisoned quote-side rate (real value
    <$0.01 — a $182M fake spike in the SDEX volume series). The FX tier
    now values BOTH legs through the resolver when possible and stores
    the SMALLER when they disagree beyond 10× — inflating a print now
    requires pumping both legs' markets with real value. The two
    poisoned rows re-derive to honest values via the generation-guarded
    corrective path.