Collaborative worldbuilding platform built on the Hive blockchain.
HiveLore enables communities to collaboratively build fictional worlds instead of isolated stories. Founders create a World Seed and World Bible, contributors submit Proposals to expand or refine the world, AI evaluates submissions for consistency, and the community determines which proposals are approved for publication. Contributors publish approved content to the Hive blockchain, where it becomes official Canon.
| Term | Definition |
|---|---|
| World Seed | The foundational concept of a fictional world, defining its premise, genre, and creative direction. |
| World Bible | The authoritative reference describing the world's rules, history, characters, locations, factions, and other canonical knowledge. |
| Proposal | A contributor-submitted request to add, modify, or expand the World Bible or existing lore. |
| Approved Proposal | A proposal that has passed community governance and is authorized for publication, but is not yet canonical. |
| Canon | Content that has been approved, published to the Hive blockchain, and indexed by HiveLore. |
| Canon Lore | An approved unit of worldbuilding, such as a character, location, faction, event, artifact, or historical record. |
| Story Chapter | A narrative set within an established world that builds upon existing canon without redefining it. |
- Hive is the immutable public record.
- PostgreSQL is the indexed application database.
- Canon is determined through community governance and finalized by on-chain publication.
- Hive rewards determine financial incentives.
- AI assists but never determines canon.
- Only approved content is published on-chain.
- Contributors remain the on-chain authors of their work.
- The MVP targets free-tier infrastructure.
- Collaborative worldbuilding
- Transparent canon governance
- Permanent authorship
- AI-assisted consistency
- Hive-native identity and rewards
- Scalable indexed blockchain architecture
Client (Next.js + React)
|
v
Fastify API
| | |
| | `-- AI Consistency Service
| |
| |---- PostgreSQL
| |
| `---- WAX + Hive Keychain / HiveSigner
|
v
Hive Blockchain
^
|
HAF / Hive Indexer
|
v
PostgreSQL
- Next.js
- React
- TanStack Query
- TipTap
- Hive Keychain
- HiveSigner
- @hiveio/wax
Modern TypeScript stack providing secure Hive authentication, efficient data fetching, and a rich editing experience.
- Node.js
- Fastify
- Prisma ORM
Responsible for authentication, governance, AI integration, indexing, moderation, and REST APIs.
- PostgreSQL
Stores drafts, proposals, AI reports, relationships, search indexes, moderation data, sessions, analytics, and indexed blockchain metadata.
- Vercel (Frontend)
- Supabase PostgreSQL
- Free Node.js hosting (Backend)
The MVP targets free-tier deployment. HAF indexing may use a public provider during development, with self-hosting considered for future production deployments.
HiveLore follows a non-custodial publishing model. Once a proposal is approved for publication, the contributor signs the final Hive transaction using Hive Keychain or HiveSigner. After the transaction is confirmed and indexed, the content becomes official canon.
- World Seeds
- World Bible versions
- Canon lore
- Story chapters
- Community discussions
- Story continuations
- Canon approvals
- Lore relationships
- Revision history
- Metadata
- Beneficiary settings
Publishing requires Hive Resource Credits (RC). Contributors are responsible for maintaining sufficient RC to publish content. Future versions may support optional account onboarding and RC delegation. The MVP assumes contributors already own a Hive account. Account creation and optional RC delegation are future enhancements.
Published blockchain data is indexed through HAF or compatible Hive indexing services and synchronized into PostgreSQL for fast queries, search, analytics, and contributor profiles.
Hive stores immutable published canon and creator attribution.
- World Seeds
- Published World Bible
- Canon lore
- Story chapters
- Canon decisions
- Creator attribution
- Rewards
- Beneficiary metadata
Stores mutable application state optimized for querying.
- Drafts
- Proposals
- Proposal comments
- AI reports
- Lore graph
- Search index
- Moderation
- Sessions
- Analytics
Hive accounts are the platform's only identity system.
Authentication flow:
- Enter Hive username.
- Sign an authentication challenge.
- Verify the signature.
- Create a secure session.
Private keys are never stored. Authentication is entirely non-custodial.
HiveLore supports Hive Keychain and HiveSigner through the same backend challenge-response flow. The API creates a short-lived, single-use, human-readable challenge containing the username, nonce, issued timestamp, expiration timestamp, configured audience, and a statement that signing does not authorize a Hive transaction or transfer. Users sign the exact challenge with posting authority. The backend verifies the signature against the account's current Hive posting authority from a trusted Hive RPC endpoint before creating or updating the local user projection.
The current verifier supports a single posting public key whose configured key weight satisfies the account's posting threshold. Accounts requiring multiple posting signatures or delegated account authorities are not accepted by the login flow yet; that avoids falsely authenticating a key that cannot independently satisfy posting authority.
Successful authentication issues a short-lived access JWT and a long-lived opaque refresh token. Refresh tokens are stored only as one-way HMAC hashes in PostgreSQL using a dedicated refresh-token secret, rotate on every successful refresh, and revoke the session family if a rotated token is reused. Browser refresh tokens are set in an httpOnly cookie and are not returned in JSON responses; access tokens are returned to the client for authenticated API requests. Authenticated API requests verify the access token session id against a non-revoked refresh session so logout invalidates already-issued access tokens. Logout is idempotent and revokes the submitted refresh session or session family when requested.
HiveLore never requests, receives, logs, stores, or returns Hive posting, active, owner, or memo private keys. Login proves identity only. It does not grant the backend permission to publish content, vote, transfer funds, or perform any on-chain action for the user.
Google authentication is an optional extension and is disabled by default. When disabled, Google routes return a not-enabled response and no Google environment variables are required. The schema includes Google identity-linking and provisioning state boundaries, but Google OAuth, automatic Hive account provisioning, and RC delegation are not part of the base MVP unless separately configured and implemented with a secure non-custodial key-delivery process.
- Contributor submits a proposal.
- AI analyzes the proposal against the current World Bible and related canon.
- Community reviews the proposal and AI report.
- Eligible contributors vote.
- Proposal reaches the approval threshold.
- Proposal becomes Approved for Publication.
- Contributor signs the final Hive transaction.
- Content is published to the Hive blockchain.
- The indexer synchronizes the published record into PostgreSQL.
- The published content becomes Canon.
AI provides advisory analysis only by comparing proposals against the current World Bible and related canon.
Checks include:
- Rule conflicts
- Timeline inconsistencies
- Duplicate lore
- Character continuity
- Location continuity
- Missing references
AI recommendations never approve or reject proposals. AI analysis uses a configurable LLM provider, allowing deployments to use available free-tier quotas or self-hosted models.
Default MVP:
- Minimum vote threshold
- 70% approval
- Voting restricted to contributors meeting minimum reputation requirements
These rules provide basic protection against Sybil attacks while keeping governance simple.
Submitted contributions enter VOTING immediately with votingStartedAt set by the backend and
votingEndsAt = votingStartedAt + 48 hours. The voting service never accepts client-supplied
voter identity, role, tally, result, or authoritative timestamps. Authenticated world members with
VOTE_ON_PROPOSAL may cast one vote per proposal and may change that vote only before
votingEndsAt.
Supported choices are APPROVE, REJECT, NEEDS_REVISION, and ALTERNATE_TIMELINE.
totalVotes counts all four choices. Approval arithmetic is exact integer basis points:
approvalNumerator = APPROVE, approvalDenominator = all four choices, and
approvalPercentageBps = floor(APPROVE * 10000 / approvalDenominator), or 0 when the denominator
is zero. NEEDS_REVISION and ALTERNATE_TIMELINE count toward both participation and the approval
denominator.
The MVP policy is centralized in apps/api/src/lib/canon-voting-policy.ts:
- Minimum eligible internal votes: 5.
- Approval threshold: 7000 basis points, so exactly 70% passes.
- Voting window: 48 hours.
- A proposal can pass only after the full window has ended.
- Founder or author status never bypasses the rule.
- A major AI warning remains attached and visible; it does not automatically reject a proposal.
If the approval rule does not pass after the window closes, the deterministic terminal outcome is
the plurality among NEEDS_REVISION, ALTERNATE_TIMELINE, and REJECT; ties resolve in that order.
If no failed-outcome choice has votes, the result is REJECTED. If the proposal's recorded base canon
version is stale at decision time, the outcome is STALE_BASE_CONFLICT and the proposal is surfaced
as needing revision instead of silently applying to newer canon.
Finalization creates one immutable ProposalDecision snapshot with per-choice counts, threshold
version, content hash, AI-warning state, branch/conflict metadata, and a deterministic decision
payload hash. Passing governance sets the proposal to APPROVED_FOR_PUBLICATION; it does not make a
LoreEntry PUBLISHED_CANON. Published canon still requires a separately confirmed Hive lore
post/comment and indexed HiveReference.
Approved decisions use a non-custodial Hive signing handoff. The API returns an unsigned posting
authority custom_json operation with HiveLore's stable custom JSON id (hivelore), schema version,
world ID, proposal ID, content hash, outcome, timestamps, tally, thresholds, AI-warning state, and
branch/base-canon references. The contributor signs and broadcasts through Hive Keychain or
HiveSigner; HiveLore never requests or stores private keys.
When a client reports broadcast details, the backend retrieves the operation through the configured
HAF/Hive indexing abstraction and verifies confirmation, operation type, custom JSON id, posting
signer, world/proposal/decision IDs, frozen tally/payload hash, timestamps, and duplicate operation
state before linking the result to HiveEvent and ProposalDecision idempotently.
Branching metadata keeps alternate continuations readable without deleting competing branches. Alternate-timeline decisions are excluded from canon-only projections; canonizing one branch does not remove other branches, which can later be revised, archived, merged, or retained under moderation permissions.
Hive votes determine:
- Visibility
- Financial rewards
- Community engagement
Hive voting never determines canon.
- World
- WorldBibleVersion
- LoreEntry
- LoreRelationship
- Proposal
- AppVote
- AIReport
- ModerationReport
- HiveReference
- HiveEvent
- User
- RewardRecord
- UserRewardSummary
- UserReputationSnapshot
- SearchIndex
REST API modules:
- Authentication
- Worlds
- Lore
- Proposal Workflow
- Voting
- AI Analysis
- Hive Integration
- Search
- Profiles
- Moderation
Implemented authentication endpoints:
POST /auth/challenge: accepts a Hive username andkeychainorhivesignerprovider, creates a five-minute challenge, and returns only the challenge ID, exact message, expiration, normalized Hive username, and provider.POST /auth/verify: accepts the challenge ID, username, provider, exact message, signature, and optional public key, verifies posting authority through Hive RPC, consumes the challenge, upserts the user projection, returns an access token, and sets the refresh cookie.POST /auth/refresh: validates the opaque refresh token from thehttpOnlycookie or request body, checks trusted browser origins for cookie-based refreshes, rotates it, revokes the previous token, returns a new access token, and revokes the session family on reuse detection.POST /auth/logout: revokes the current refresh session or session family when requested and clears the refresh cookie. Repeated logout is safe.GET /me: requires a valid access JWT and returns the safe canonical Hive-linked user representation.
Auth challenge, verification, and refresh endpoints use Fastify rate limiting backed by PostgreSQL buckets so limits are shared across API instances. Set TRUST_PROXY=true on deployments that run behind a trusted proxy or load balancer so client IPs are resolved from trusted forwarding headers.
Google auth routes are not registered as a default login path. With GOOGLE_AUTH_ENABLED=false, /auth/google/* returns a not-enabled response. Enabling Google currently exposes the guarded boundary only; Google OAuth account linking and automatic Hive provisioning remain deferred until secure non-custodial account creation and key delivery are specified.
Implemented world endpoints:
POST /worlds: requires authentication, creates a mutable off-chain World Seed and initialWorldBibleVersionin one transaction, and assigns the authenticated user an activeFOUNDERmembership. The route never accepts a client-supplied founder or role and does not publish to Hive.GET /worlds: browses discoverable worlds with pagination and basic title/description, genre, and tone filters.GET /worlds/:worldId: returns one world with its founder, seed, and current latest World Bible version.GET /worlds/:worldId/hub: returns the World Hub aggregate for the frontend, including world seed, current bible preview, simple canon/proposal stats, and latest canon lore entry summaries.PATCH /worlds/:worldId: requiresEDIT_INITIAL_CANONworld permission and updates mutable off-chain world title/description, seed, and latest initial bible content only. It does not create proposals, canonize content, or publish to Hive.
Implemented contribution endpoints:
POST /worlds/:worldId/contributions: requires authentication andCREATE_LORE_DRAFTworld permission, creates an author-owned structured contribution draft for lore or story content, and optionally links it to a lore entry in the same world.GET /worlds/:worldId/contributions: requires authentication and lists only the authenticated writer's contributions in the world with page/pageSize pagination and optional status/kind filters.GET /worlds/:worldId/contributions/:contributionId: returns an authenticated writer's own contribution draft or submitted contribution without exposing other writers' private drafts.PATCH /worlds/:worldId/contributions/:contributionId: requiresEDIT_OWN_DRAFT, updates only mutable draft fields, and rejects submitted contributions.DELETE /worlds/:worldId/contributions/:contributionId: requiresEDIT_OWN_DRAFT, deletes only draft contributions, and never deletes submitted proposals.POST /worlds/:worldId/contributions/:contributionId/submit: requiresSUBMIT_PROPOSAL, atomically locks the draft, creates exactly one submitted proposal with an immutableproposedContentsnapshot, and does not publish to Hive.
Implemented proposal discussion endpoints:
GET /worlds/:worldId/proposals/:proposalId/comments: lists flat chronological off-chain proposal comments with cursor pagination. The proposal must belong to the route world. Deleted comments are returned as tombstones without the original body.POST /worlds/:worldId/proposals/:proposalId/comments: requires authentication and the existingVOTE_ON_PROPOSALworld permission, derives the author from the verified session, trims plain-text body content, enforces a 3,000-character maximum, and rate-limits comment writes to 5 per verified user per minute by default.
Proposal comments are stored in PostgreSQL as mutable discussion records. They are not Hive comments, not immutable canon records, and never count as AppVote rows, approval totals, proposal outcomes, reputation, rewards, or canon status. Soft deletion preserves the discussion audit shape while normal API responses hide moderated comment bodies. Comment write rate limits use the existing PostgreSQL-backed Fastify rate-limit store in the API app, so limits are shared across API instances that use the same database.
Application reputation is independent of Hive reputation and is computed from application events and indexed Hive activity.
The reputation indexer aggregates:
- Accepted canon contributions
- Approved proposals
- Community participation
- Voting activity
- Founder activity
- Moderator actions
- Moderation history
Reputation is recalculated from historical events, making it transparent, auditable, and reproducible.
Reputation influences governance eligibility, contributor recognition, and future reputation-weighted features.
Hive remains the financial reward layer.
Approved canon is published by its contributor with beneficiary settings defining reward distribution.
| Recipient | Share |
|---|---|
| Contributor | 90% |
| World Founder | 10% |
| Platform | 0% |
Indexed Hive reward data powers contributor profiles, leaderboards, analytics, and historical reward tracking without influencing canon governance.
Current Stage: MVP infrastructure scaffold
The repository currently contains foundation infrastructure only. Product features described in the vision and roadmap are planned for later work and are not implemented in this scaffold.
The web workspace now includes a UI foundation: shared responsive application shell, reusable UI primitives, loading/empty/error states, minimal placeholder routes, route-level loading/error/not-found states, theme switching, official Hive asset usage, TanStack Query/API client wiring, and a TipTap editor scaffold. See apps/web/README.md for frontend-specific usage and boundaries.
- Core worldbuilding
- Canon governance
- Hive publishing
- Reputation system
- Search
- Mobile application
- Multi-chain support
- Interactive maps
- AI story generation
- Automated account onboarding
- RC delegation
Sprint Goal
------- -------------------------------
0 Architecture & Infrastructure
1 Hive Authentication
2 World & Lore
3 AI Consistency
4 Governance Workflow
5 Hive Publishing
6 Blockchain Indexing
7 Reputation & Profiles
8 Search & Discovery
9 QA & MVP Release
- Interactive maps
- Reputation-weighted governance
- AI world simulation
- Advanced analytics
- Plugin ecosystem
- Public developer API
- Account onboarding and RC delegation
- Self-hosted HAF infrastructure
- Node.js 22 LTS or newer
- npm 10 or newer
- Git
git clone <repository-url>
cd hivelore
npm installCreate local environment files from the checked-in examples. Never commit real secrets, and never use NEXT_PUBLIC_ for secret values.
Windows Command Prompt:
copy .env.example .env
copy apps\web\.env.example apps\web\.env.local
copy apps\api\.env.example apps\api\.envPowerShell:
Copy-Item .env.example .env
Copy-Item apps/web/.env.example apps/web/.env.local
Copy-Item apps/api/.env.example apps/api/.envmacOS/Linux:
cp .env.example .env
cp apps/web/.env.example apps/web/.env.local
cp apps/api/.env.example apps/api/.envSupabase provides the hosted PostgreSQL database for the MVP. Prisma lives in the API workspace and is the backend ORM and migration system.
Hive remains the immutable source of truth for published canon, on-chain authorship, rewards, beneficiary metadata, and blockchain events. PostgreSQL stores mutable application state plus indexed and derived projections used for fast API queries.
Application source data includes worlds, World Bible draft/version state, lore draft/application state, lore relationships, proposals, app votes, advisory AI reports, moderation reports, user profile metadata, application reputation snapshots, and PostgreSQL search rows. Indexed or derived projections include Hive references, Hive events, reward records, reward summaries, verified on-chain publication metadata, on-chain authorship, beneficiary metadata, and reward data. Projection tables must be rebuildable from Hive or source application records.
Add these server-only variables to apps/api/.env. Never commit real values, never prefix them with NEXT_PUBLIC_, and never put database passwords in frontend environment files.
# PostgreSQL application connection through the Supabase pooler.
DATABASE_URL="postgresql://USER:PASSWORD@POOLER_HOST:6543/postgres?pgbouncer=true"
# Direct PostgreSQL connection used for Prisma migrations and administrative operations.
DIRECT_URL="postgresql://USER:PASSWORD@DIRECT_HOST:5432/postgres"
# Hive authentication and secure session settings.
AUTH_JWT_SECRET="replace-with-a-long-random-secret"
AUTH_REFRESH_SECRET="replace-with-a-different-long-random-secret"
AUTH_JWT_ISSUER="hivelore"
AUTH_JWT_AUDIENCE="hivelore-web"
AUTH_ACCESS_TOKEN_TTL_SECONDS=900
AUTH_REFRESH_TOKEN_TTL_SECONDS=1209600
AUTH_CHALLENGE_TTL_SECONDS=300
AUTH_COOKIE_DOMAIN=
AUTH_COOKIE_SECURE=true
HIVE_AUTH_AUDIENCE="hivelore-local-api"
ERROR_TRACKING_ENABLED=false
ERROR_TRACKING_WEBHOOK_URL=
# Hive integration defaults to public development endpoints.
HIVE_RPC_URL="https://api.hive.blog"
HAF_API_URL="https://api.hive.blog/hafbe-api"
HIVELORE_APP_ID="hivelore/0.1.0"
HIVE_MAINNET_CHAIN_ID="beeab0de00000000000000000000000000000000000000000000000000000000"
HIVE_MAINNET_RPC_NODES="https://api.hive.blog"
HIVE_MAINNET_HAF_URL="https://api.hive.blog/hafbe-api"
HIVE_BROADCAST_MAX_ATTEMPTS=4
HIVE_BROADCAST_INITIAL_DELAY_MS=500
HIVE_BROADCAST_MAX_DELAY_MS=5000
HIVE_BROADCAST_BACKOFF_MULTIPLIER=2
HIVE_BROADCAST_JITTER_RATIO=0.2
HIVE_BROADCAST_TIMEOUT_MS=10000
HIVE_BROADCAST_TOTAL_DEADLINE_MS=90000
HIVE_CONFIRMATION_POLL_INTERVAL_MS=3000
HIVE_CONFIRMATION_TIMEOUT_MS=60000
HIVE_NODE_MAX_CONSECUTIVE_FAILURES=1
HIVE_NODE_COOLDOWN_MS=30000
# Optional Google-to-Hive linking and onboarding. Disabled for the base MVP.
GOOGLE_AUTH_ENABLED=false
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REDIRECT_URI=
GOOGLE_HIVE_PROVISIONING_ENABLED=false
# Optional RC delegation. Disabled for the base MVP.
HIVE_RC_DELEGATION_ENABLED=false
HIVE_RC_DELEGATOR_ACCOUNT=
HIVE_RC_DELEGATION_AMOUNT=DATABASE_URL is the pooled connection used by normal API queries. DIRECT_URL is the direct PostgreSQL connection Prisma uses for migrations and administrative operations. Supabase service-role keys are not required for Prisma's PostgreSQL connection. Hive private keys must never be stored.
Use separate credentials and databases for development and production. Any internet-facing API environment, including shared development hosts, must set long random AUTH_JWT_SECRET and AUTH_REFRESH_SECRET values and AUTH_COOKIE_SECURE=true. Deployments behind a trusted proxy or load balancer must set TRUST_PROXY=true. Do not reset a shared development database without team coordination.
- Create or select a Supabase project.
- Open the project's database connection settings.
- Copy the pooled application connection string for normal API queries.
- Copy the direct PostgreSQL connection string for Prisma migrations.
- Put both values in
apps/api/.env. - Keep
apps/api/.envignored and store deployed API credentials only in the API hosting provider's secret settings.
Supabase dashboard labels can vary, but the important distinction is pooled application access versus direct migration access.
Run database commands from the repository root:
npm run db:generate
npm run db:validate
npm run db:format
npm run db:migrate
npm run db:migrate:deploy
npm run db:seed
npm run db:studio
npm run db:resetdb:generategenerates the API-local Prisma Client.db:validatevalidatesapps/api/prisma/schema.prisma.db:formatformats the Prisma schema.db:migratecreates and applies a development migration usingDIRECT_URL.db:migrate:deployapplies committed migrations in hosted environments.db:seedruns the deterministic fictional development seed.db:studioopens Prisma Studio.db:resetis destructive. It drops and recreates the configured database schema, reapplies migrations, and runs the seed only after Prisma's confirmation prompt.
npm install
npm run db:generate
npm run db:migrate
npm run db:seed
npm run devBefore running migration or seed commands, create apps/api/.env with safe development Supabase credentials. Do not use production credentials for local destructive testing.
Edit apps/api/prisma/schema.prisma, then run:
npm run db:format
npm run db:validate
npm run db:migrateReview generated SQL under apps/api/prisma/migrations, commit the schema and migration together, and use npm run db:migrate:deploy in hosted API environments. Do not manually create production tables in the Supabase dashboard as a substitute for migrations.
Feature branches must include migration files. Resolve migration conflicts before merge.
20260718131500_init_hivelore_schema is the authoritative Prisma baseline. A later historical migration, 20260718154842_init, accidentally duplicated the same baseline SQL and caused fresh PostgreSQL databases to fail during prisma migrate deploy when the second migration tried to recreate enums, tables, indexes, and foreign keys that already existed.
The duplicate migration directory is intentionally retained, but its SQL body is a no-op. This preserves the historical migration name while allowing a new developer, CI job, preview environment, or empty hosted database to run:
npm run db:migrate:deploy
npm run db:generateExisting databases that already recorded both baseline migrations do not need data changes before deploying this repository repair. Prisma may warn that 20260718154842_init was modified after it was applied because the stored checksum reflects the old duplicate SQL. That warning is expected for those environments; do not reset or recreate the database to clear it. If an environment is blocked by a failed 20260718154842_init row after the duplicate SQL already created the baseline objects, inspect _prisma_migrations and the actual schema first, then run npx prisma migrate resolve --rolled-back 20260718154842_init --schema apps/api/prisma/schema.prisma before npm run db:migrate:deploy. If an environment has only the first baseline recorded, the retained no-op second migration can be applied normally.
CI validates the full migration history against a disposable PostgreSQL database with:
TEST_DATABASE_ADMIN_URL=postgresql://postgres:postgres@127.0.0.1:5432/postgres npm run db:migrate:check:freshThe check creates a uniquely named hivelore_migrate_check_* database, runs prisma migrate deploy twice, compares the deployed schema with schema.prisma, generates Prisma Client, runs the development seed, smoke-checks representative tables/enums/constraints/indexes, and drops only the generated disposable database.
CI also validates the upgrade path from develop with npm run db:migrate:check:upgrade. That job deploys the base branch migration history into a disposable PostgreSQL database, applies the documented 20260718154842_init recovery when needed, deploys this branch's repaired migrations, checks for schema drift, and drops the disposable database.
The seed at apps/api/prisma/seed.ts is development-only and contains fictional Hive usernames, world content, lore, one lore relationship, one submitted proposal, and an advisory AI report. It does not seed real people, credentials, Hive private keys, real blockchain transactions, or financial payout projections.
Hive is authoritative for on-chain publication, authorship, rewards, beneficiary metadata, and blockchain events. HiveReference, HiveEvent, RewardRecord, and UserRewardSummary are indexed or derived database projections. A local PostgreSQL flag or timestamp is never sufficient to prove canon; canon requires an approved publication that has been published to Hive and indexed by HiveLore.
All backend Hive access goes through apps/api/src/lib/hive. The module uses @hiveio/wax for transaction build, serialization, signing handoff, and broadcast; it exposes signer abstractions for Hive Keychain and HiveSigner flows without storing private keys. HAF reads use the configurable read-only HAF_API_URL client and are normalized before projection into PostgreSQL. The initial default is Hive's public HAF Block Explorer endpoint, using documented HAFBE paths such as /last-synced-block, /block-search, and /accounts/{account}/operations/comments/{permlink}; self-hosted HAF remains deferred.
HiveLore is mainnet-only. It uses chain ID beeab0de00000000000000000000000000000000000000000000000000000000, address prefix STM, and the configured HIVE_MAINNET_RPC_NODES. The network config is built as one unit with chain ID, node list, HAF/indexer URL, address prefix, custom JSON ID, and transaction expiration policy. Empty node lists, malformed chain IDs, non-mainnet chain IDs, credential-bearing node URLs, and non-HTTPS production nodes fail startup validation.
Backend-controlled broadcasts go through the typed reliability layer in apps/api/src/lib/hive/broadcast-reliability.ts. It broadcasts only already-signed transactions, never asks for private keys, and preserves the submitted transaction ID across retries. Transient RPC failures use bounded exponential backoff with jitter and node rotation; permanent transaction rejections such as invalid signatures, insufficient authority, malformed operations, expired transactions, and insufficient Resource Credits fail fast without rotating through every node. Ambiguous outcomes, including a timeout after a node may have accepted a transaction, enter confirmation polling before any retry and retry only the exact same signed transaction when still safe.
An RPC broadcast acknowledgment is not confirmation. For the MVP, "confirmed" means included in a Hive block and located through HAF or a compatible indexing/read-back path; this is weaker than last-irreversible-block finality and is reported as block inclusion. Confirmation verifies the transaction ID, operation index when known, HiveLore operation type, custom_json ID, posting signer, world/proposal/decision IDs, and frozen payload hash before persistence. Client-supplied block numbers and timestamps are treated only as lookup hints; PostgreSQL state changes use chain/read-back data. Confirmation time is application time after verification, while blockchain timestamp comes from the block/indexer row.
Successful confirmed broadcasts return a structured result containing the network, transaction ID, block number, operation indexes, blockchain timestamp, confirmation time, confirmation source, sanitized node URL when applicable, and attempt count. Timeouts return an unknown or delayed-confirmation state; they must be reconciled by retrying confirmation for the same transaction ID, not by creating a different transaction.
Mainnet smoke test:
- Build the harmless HiveLore
custom_jsonoperation with IDhivelore_smoke. - Use posting authority only:
required_authsmust be empty andrequired_posting_authsmust contain the signing account. If active or owner authority is requested, treat it as a bug and stop. - Use this exact JSON payload, which contains no personal data or secrets:
{
"app": "hivelore",
"type": "mainnet_smoke",
"version": 1,
"purpose": "broadcast_readback_verification"
}- Sign from the local process environment only. Never hardcode, print, persist, or commit a Hive private key.
- Broadcast through the existing WAX client and reliability broadcaster, preserving multi-node RPC failover.
- Confirm through
condenser_api.get_transaction, verify transaction ID, signer,custom_jsonID, posting authority, and payload, then readblock_numfrom that transaction. - Call
condenser_api.get_block_header(block_num)and use that header timestamp as the blockchain timestamp. Never use transaction expiration as the blockchain timestamp. - Print only the transaction ID, block number, block timestamp, RPC node used, and pass/fail verification summary after the irreversible broadcast.
Run the real smoke test only after reviewing the account and permanence warning:
$env:HIVE_SMOKE_ACCOUNT = "<your-hive-account>"
$env:HIVE_SMOKE_POSTING_KEY = "<your-posting-private-key>"
npm run smoke:hive-mainnetThe command prompts for BROADCAST HIVELORE SMOKE immediately before broadcasting. Mainnet smoke tests must not be run without explicit approval of the account, exact operation, and expected permanence.
The API workspace includes a resumable HAF sync skeleton. It reads HAF block-search pages, projects supported HiveLore operations into HiveEvent, and stores its durable resume point in IndexerWatermark.
npm run indexer:run --workspace=@hivelore/apiOptional API environment variables:
INDEXER_NAME=hivelore-haf
INDEXER_START_BLOCK=1
INDEXER_BATCH_SIZE=100
INDEXER_MAX_BLOCKS_PER_RUN=1000
INDEXER_MAX_READY_LAG_BLOCKS=1200Run npm run db:generate after pulling schema changes, then apply the committed Prisma migrations before running it against a real database.
GET /health is the lightweight liveness endpoint. GET /ready verifies PostgreSQL connectivity and reports HAF indexer lag against INDEXER_MAX_READY_LAG_BLOCKS; it returns 503 when any dependency is degraded. API logs are structured JSON with an x-request-id request identifier, and ERROR_TRACKING_ENABLED=true posts unhandled error summaries to ERROR_TRACKING_WEBHOOK_URL without request headers or secret-bearing bodies.
npm run devLocal services:
- Web: http://localhost:3000
- API: http://localhost:3001
- Health: http://localhost:3001/health
- Readiness: http://localhost:3001/ready
Run one application at a time:
npm run dev:web
npm run dev:apinpm run format:check
npm run lint
npm run typecheck
npm run buildAdditional root commands:
npm run format
npm run lint:fix
npm run cleanWorkspace examples:
npm run build --workspace=@hivelore/web
npm run start --workspace=@hivelore/apihivelore/
|-- apps/
| |-- api/
| | |-- src/
| | | |-- config/
| | | | `-- env.ts
| | | |-- routes/
| | | | `-- health.ts
| | | |-- app.ts
| | | `-- server.ts
| | |-- .env.example
| | |-- package.json
| | `-- tsconfig.json
| `-- web/
| |-- public/
| |-- src/
| | |-- app/
| | | |-- globals.css
| | | |-- layout.tsx
| | | `-- page.tsx
| | |-- components/
| | | |-- editor/
| | | |-- layout/
| | | |-- states/
| | | `-- ui/
| | `-- lib/
| | |-- api/
| | |-- query/
| | `-- env.ts
| |-- .env.example
| |-- README.md
| |-- next-env.d.ts
| |-- next.config.ts
| |-- package.json
| `-- tsconfig.json
|-- packages/
| `-- config/
| `-- package.json
|-- .github/
| `-- workflows/
| `-- ci.yml
|-- .husky/
| `-- pre-commit
|-- .vscode/
| |-- extensions.json
| `-- settings.json
|-- .editorconfig
|-- .env.example
|-- .gitignore
|-- .prettierignore
|-- eslint.config.mjs
|-- package.json
|-- package-lock.json
|-- prettier.config.mjs
|-- tsconfig.base.json
`-- README.md
Directory roles:
apps/webis the only frontend application. It is a Next.js App Router scaffold.apps/apiis the only backend application. It is a Fastify TypeScript API with liveness and readiness endpoints.apps/api/prismacontains the canonical API Prisma schema, migrations, and development seed.packagesis reserved for shared workspace packages.packages/configis intentionally minimal until real shared configuration is needed..github/workflowscontains CI checks for formatting, linting, type-checking, and builds..huskycontains the pre-commit hook, which runslint-stagedonly on staged files..vscodecontains editor recommendations and shared formatting/linting settings.
apps/webis the only frontend application.apps/apiis the only backend application.- The root
package.jsoncoordinates npm workspaces. - The root
package-lock.jsonis the only npm lockfile. - Root configuration files are shared unless a framework-specific file is necessary.
eslint.config.mjs,prettier.config.mjs, andtsconfig.base.jsonare the shared configuration entry points.
.env.example,apps/web/.env.example, andapps/api/.env.exampledocument safe placeholder values.- Production web variables belong in Vercel.
- Production API variables belong in the API host.
- CI secrets belong in GitHub Actions secrets.
- Hive private keys must never be collected, stored, transmitted, or logged.
NEXT_PUBLIC_variables are public browser-exposed values and must never contain secrets.- The frontend reads
NEXT_PUBLIC_API_URL; it is a placeholder until the Fastify API has a production deployment.
Create a Vercel project for the web app with these monorepo settings:
- Root Directory:
apps/web - Framework Preset:
Next.js - Install Command:
cd ../.. && npm ci - Build Command:
cd ../.. && npm run build --workspace=@hivelore/web - Output Directory: leave as the Next.js default
Required Vercel environment variable:
NEXT_PUBLIC_API_URL=<deployed-api-url-placeholder>There is no production API deployment in this scaffold. Replace the placeholder with the deployed Fastify API URL when that later infrastructure exists.
Infrastructure owner: Perla
Web package: Kareem, collaborating with Perla