Built by 2 parallel agents (+ image-API research):
- @pyre/prometheus: generateSpawn() engine — deterministic §9 meta-mixer
(40/25/20/15), prompt builder ("inspired mutation, not a clone" + no
people/brands), name/ticker/lore/tagline gen, image-prompt, denylist + moderation
safety. PROVIDER-ABSTRACTED (TextProvider/ImageProvider/ModerationProvider) with
deterministic STUBS so it runs keyless today; real call shapes documented (Claude
Haiku text · FLUX schnell image · OpenAI omni-moderation). 13 tests.
- @pyre/db: migration 002 (prometheus_generations, spawn_records) + record/list/get.
- @pyre/api: admin-gated POST /api/prometheus/generate + /api/spawn/launch
(x-admin-token; CLOSED with 403 when ADMIN_API_TOKEN unset; timing-safe compare),
public GET /api/spawns + /api/spawn/:id.
- @pyre/web: public /spawn record page; @pyre/core SpawnRecord type.
Verified: typecheck 8/8, 134 tests (core 91 + prometheus 13 + solana 30), web build
(+/spawn), migrate 002 live, /api/spawns OK, admin gate returns 403 (unconfigured).
Follow-ups: set ADMIN_API_TOKEN to use admin endpoints; wire real provider keys;
receiptId→DB-id wiring; admin generation UI.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pyre/db
Postgres-backed Essence ledger for PYRE. A small typed data layer over
pg (no ORM): a lazily-created connection pool, an idempotent migration
runner, and the round / receipt / contribution query surface.
Trust rules
- Connection details come from the environment (
DATABASE_URL) or an explicit argument — credentials are never hardcoded. The localhost dev URL is only a last-resort fallback. - Recovered ATA rent is not Essence.
cleanup_receiptsrecords rent returned to the user; it never touches a round total. Onlyessence_contributions(the protocol fee and explicit opt-in contributions) feedrounds.essence_lamports. - Parameterized queries only (
$1,$2, …) — no string interpolation. - Lamport amounts cross the API boundary as decimal strings (u64-safe) and
are cast to
::bigintin SQL. - No network/DB access at import time. The pool is created lazily;
migrate()is safe to call repeatedly.
Tables
Defined in migrations/001_init.sql (idempotent, CREATE TABLE IF NOT EXISTS).
rounds
| column | type | notes |
|---|---|---|
id |
BIGSERIAL |
primary key |
status |
TEXT |
'open' | 'closed', default 'open' |
essence_lamports |
BIGINT |
running round total, default 0 |
started_at |
TIMESTAMPTZ |
default now() |
closed_at |
TIMESTAMPTZ |
nullable |
cleanup_receipts
| column | type | notes |
|---|---|---|
id |
BIGSERIAL |
primary key |
wallet |
TEXT |
|
tx_signature |
TEXT |
unique (idempotency key) |
kind |
TEXT |
'close' | 'burn' |
rent_returned_lamports |
BIGINT |
rent returned to the user |
fee_lamports |
BIGINT |
protocol fee, default 0 |
closed_accounts |
JSONB |
array of addresses, default '[]' |
created_at |
TIMESTAMPTZ |
default now() |
Index: cleanup_receipts(wallet).
essence_contributions
| column | type | notes |
|---|---|---|
id |
BIGSERIAL |
primary key |
round_id |
BIGINT |
FK → rounds(id) |
wallet |
TEXT |
|
tx_signature |
TEXT |
unique (idempotency key) |
lamports |
BIGINT |
amount fed to the PYRE |
kind |
TEXT |
'fee' | 'contribution' |
created_at |
TIMESTAMPTZ |
default now() |
Index: essence_contributions(round_id).
API
import {
getPool,
migrate,
ensureOpenRound,
recordReceipt,
recordEssence,
getEssenceSummary,
closePool,
} from "@pyre/db";
getPool(databaseUrl?): Pool— lazily create and cache the singletonpg.Pool. Connection string resolves to the explicit argument, thenDATABASE_URL, then the localhost dev default. No connection is opened until first query.migrate(): Promise<void>— apply everymigrations/*.sqlin name order, each in its own transaction. Idempotent; safe to call repeatedly.ensureOpenRound(): Promise<{ id: string }>— return the current open round, creating one if none exists.recordReceipt(r): Promise<void>— insert a cleanup receipt ({ wallet, txSignature, kind: 'close'|'burn', rentReturnedLamports, feeLamports, closedAccounts }). Idempotent ontxSignature. Does not affect any round total.recordEssence(e): Promise<{ recorded, roundId }>— record a contribution ({ wallet, txSignature, lamports, kind: 'fee'|'contribution' }) against the open round. In one transaction: ensures a round, inserts (idempotent ontxSignature), and incrementsrounds.essence_lamportsonly when a new row is inserted. Returnsrecorded: falsefor duplicate signatures.getEssenceSummary(): Promise<EssenceSummary>— open-round{ roundId, totalLamports, contributionCount, recent }, whererecentis the last ~10 contributions (newest first).closePool(): Promise<void>— close and clear the pool for shutdown / test teardown.
All lamport amounts are strings in and out.
Usage
await migrate();
await recordEssence({
wallet: "Wallet…",
txSignature: "Sig…",
lamports: "1000000",
kind: "fee",
});
const summary = await getEssenceSummary();
Migrations
SQL lives in migrations/, one forward migration per file in lexical order
(001_init.sql, 002_…sql, …). Each file must be idempotent. The runner
(migrate()) applies them in name order against the resolved connection.