Skip to content

cenetex/app-ruby-high

Repository files navigation

Ruby High

A school where the teachers grade you in their own voice. Clear daily classes, bank grades, and keep the yearbook.

Ruby High is a standalone Node service and installable SPA. Ruby hosts the school; specialist faculty (Sally Science, Professor Edward) teach their domains; six AI classmates sit beside you. You play a generated character, build school disciplines and virtues, walk between rooms, clear daily classes and practice questions, collect hidden First Bell comic pages, and graduate after Senior year.

For the product story, the mechanics, the cast, and the roadmap, see DESIGN.md. This file is the runbook.

Production is on Fly.io; see infra/fly-deploy.md. AWS App Runner is legacy / manual-only; see infra/README.md.

Run it locally

npm install
npm run build
npm run dev:server

Open http://127.0.0.1:3000/api/apps/ruby-high/viewer. Normal play starts with a Ruby High session cookie; OpenRouter sign-in is still available for BYOK AI (PKCE, your own key, no card). If Privy is configured, the Account button signs the player in and can connect or reuse a Solana wallet; this build does not auto-create one on login. Browser-owned OpenRouter keys default to sessionStorage, can opt into localStorage persistence with rh_openrouter_persist=1, and are never held by the server. Game state, auth sessions, and session-scoped packs persist through the configured store; teacher chat transcripts are process-local and reset on server restart/deploy.

Ruby High 2.0 C wedge

The first C implementation lives in ruby2/c. It is a deterministic engine and native SDL slice, not the final sokol client yet. It proves fixed-size state, effect payload reduction, the Source/Sense/Sync/Signal disciplines, RPN branch gates, item validation, empty-start inventory slots, whole-campus pointcrawl navigation, ranker-curated two-button action trays with four-button class moments, two-option conversation branches, Yearbook candidate eviction, archetype resolution, companion locks, gameplay divergence tests, and the first Captain Null trace resolver. play-llm adds the local Ollama vertical slice: deterministic approach-choice gameplay with bounded speech-bubble lines from ruby-high-local and authored fallback copy if the model is unavailable or leaks analysis instead of JSON.

make -C ruby2/c test
make -C ruby2/c gameplay-test
make -C ruby2/c llm-test
make -C ruby2/c run
make -C ruby2/c play
make -C ruby2/c play-llm
make -C ruby2/c native-smoke
make -C ruby2/c native-run

The standalone viewer is installable as a PWA from /api/apps/ruby-high/viewer. The service worker is scoped to /api/apps/ruby-high/, caches the shell and core assets, and keeps auth, chat, pack management, and session state requests network-only. Full offline gameplay still requires the Ruby High server because the authoritative school state lives there.

Offline SPA

Ruby High also has a static SPA build for browser-only offline testing:

npm run build:spa
npm run spa:dev

Open http://127.0.0.1:4173. This build packages the same viewer shell with a browser-local offline API shim backed by localStorage and the bundled Ruby/Sally/Edward question banks. Core classroom play, character creation, room switching, Merit Stars, and local persistence work without the hosted server. OpenRouter auth, Hall Pass purchases, portrait/diploma image generation, teacher publishing, and hosted account sync still require the Node service.

For offline text AI, run Ruby High against a local OpenAI-compatible chat-completions server such as Ollama:

ollama create ruby-high-local -f /path/to/Modelfile
ollama serve
RUBY_HIGH_LLM_PROVIDER=local \
RUBY_HIGH_LLM_BASE_URL=http://127.0.0.1:11434/v1 \
RUBY_HIGH_LLM_MODEL=ruby-high-local \
npm run dev:server

Local mode removes the OpenRouter key requirement for text chat, teacher turns, NPC chimes, character text, opinion grading, and multiple-choice distractors for source cards. Portrait and diploma image generation still use the OpenRouter image endpoint. A browser-owned OpenRouter key stays BYOK/free-to-Ruby-High; the optional server-hosted image path spends Hall Passes.

The offline SPA uses the same default local model and lets you override the local endpoint in DevTools:

localStorage.setItem("ruby-high:local-llm-base", "http://127.0.0.1:11434/v1");
localStorage.setItem("ruby-high:local-llm-model", "ruby-high-local");

Native desktop/mobile packaging is intentionally not part of the current retention-truth build. Reintroduce it only when public-web retention data justifies that surface.

Dev endpoints

No hosted account or OpenRouter key is needed for these:

  • GET /dev/pick — draw a question for the active faculty.
  • GET /dev/pick?faculty=sally-science&difficulty=hard — filter the draw.
  • GET /dev/faculty — roster + question counts.
  • GET /dev/clear — wipe the board (keeps Merit Stars).
  • GET /dev/reset — wipe the session (Merit Stars + history).

Production app endpoints

  • GET /api/apps/ruby-high/admin renders a browser dashboard for the token-gated usage snapshot, 14-day charts, and an operator overview. Paste the admin token once; the page stores it locally and calls admin endpoints with a Bearer header.
  • POST /api/apps/ruby-high/metrics/event records first-party viewer events. The bundled viewer sends durable app_open on boot and session_resume after returning from five-plus minutes inactive.
  • GET /api/apps/ruby-high/admin/metrics returns a compact JSON snapshot for retention tuning: auth identity records/sessions, visitor counts, 14-day auth/play/event series, Ruby High session progression, visitor and character D1 retention, durable v4 metric events, and in-process log counters. auth.users is identity records, not unique humans. It is disabled until RUBY_HIGH_ADMIN_TOKEN is set and accepts either Authorization: Bearer <token> or the exact token value.
  • GET /api/apps/ruby-high/admin/metrics/schema publishes the current admin metrics contract (ruby-high-admin-metrics.v4): field semantics, reliability levels, caveats, and the durable event streams for traffic, retention, funnel, commerce, LLM, and errors.
  • GET /api/apps/ruby-high/admin/overview returns a token-gated LLM-generated operator overview built only from aggregate metrics. It requires the normal server LLM credential.
  • GET /api/apps/ruby-high/yearbook/:shareId/:grade renders a static public yearbook card for a sealed grade. Sealed year cards expose Open/Copy controls in the viewer. ?format=json returns card data and ?format=svg returns the social image. ?format=png is intentionally 501 until server-side raster rendering is configured.

Service Wiring

The standalone server starts four services (FacultyService, RubyHighService, AuthService, ChatService) backed by the content-pack registry under src/content/. Ruby High Original is always the base school; public creator packs rotate into one Guest Faculty course automatically each week, or can be set as a user override from the Guest Faculty screen.

Configuration

Knob Default Notes
PORT 8080 HTTP port.
HOST 0.0.0.0 Bind address.
RUBY_HIGH_PUBLIC_BASE http://localhost:3000 Public URL the app is reachable at. Must be HTTPS in production — OpenRouter rejects HTTP callbacks.
RUBY_HIGH_PRIVY_APP_ID Enables Privy account sign-in when set with RUBY_HIGH_PRIVY_CLIENT_ID and one server verifier secret.
RUBY_HIGH_PRIVY_CLIENT_ID Public Privy client id embedded in the viewer so the browser SDK can initialize.
RUBY_HIGH_PRIVY_LOGIN_METHODS email,wallet,google,twitter,passkey Comma-separated Privy login methods shown in the viewer. Use google for Gmail sign-in. Each method must also be enabled in the Privy dashboard.
RUBY_HIGH_PRIVY_APP_SECRET Preferred server-side Privy secret for verifying tokens and fetching linked wallet/user details. Set via secrets only.
RUBY_HIGH_PRIVY_VERIFICATION_KEY Optional JWT verification-key fallback for deployments that do not use RUBY_HIGH_PRIVY_APP_SECRET.
RUBY_HIGH_STORE_BACKEND json json for local dev (atomic file at ~/.ruby-high/state.json), dynamodb for production.
RUBY_HIGH_STATE_PATH ~/.ruby-high/state.json JSON-backend file path.
RUBY_HIGH_DYNAMO_TABLE Required when backend is dynamodb.
AWS_REGION Required when backend is dynamodb.
RUBY_HIGH_STATE_TTL_SECONDS 90 days DynamoDB TTL for idle sessions.
RUBY_HIGH_ADMIN_TOKEN Enables /api/apps/ruby-high/admin/metrics. Keep this in secrets only.
RUBY_HIGH_METRICS_TRUST_START Optional ISO date/time shown in admin metric quality notes after a metrics reset or schema migration.
RUBY_HIGH_LLM_PROVIDER openrouter Set to local to use a local OpenAI-compatible /v1/chat/completions endpoint. Also inferred as local when RUBY_HIGH_LLM_BASE_URL is set.
RUBY_HIGH_LLM_BASE_URL http://127.0.0.1:11434/v1 in local mode Local OpenAI-compatible base URL. Values ending in /v1 or /chat/completions are both accepted.
RUBY_HIGH_LLM_MODEL ruby-high-local in local mode Model id sent to the local endpoint. Many single-model servers ignore it, but OpenAI-compatible servers require the field.
RUBY_HIGH_LLM_API_KEY local in local mode Optional bearer token for local servers configured with an API key.
RUBY_HIGH_STUDENT_MODEL google/gemini-3.5-flash Model used for NPC opinion responses.
RUBY_HIGH_OPENROUTER_API_KEY Optional server-side OpenRouter key for hosted AI Access and hosted portrait/diploma generation. Server-hosted text AI is available only while the signed-in session has active AI Access. Browser-owned OpenRouter keys remain BYOK and do not spend Hall Passes.
RUBY_HIGH_OPENROUTER_REFERER https://ruby-high.local Sent in OpenRouter request headers.
RUBY_HIGH_OPENROUTER_TITLE Ruby High Sent in OpenRouter request headers.
RUBY_HIGH_STRIPE_SECRET_KEY Enables web Hall Pass purchases via Stripe Checkout.
RUBY_HIGH_STRIPE_WEBHOOK_SECRET Required for /api/apps/ruby-high/billing/stripe/webhook to grant Hall Passes after paid Checkout Sessions.
RUBY_HIGH_STRIPE_CURRENCY usd Currency for built-in Hall Pass packs.
RUBY_HIGH_HALL_PASS_5_CENTS 199 Price for 5 Hall Passes.
RUBY_HIGH_HALL_PASS_20_CENTS 699 Price for 20 Hall Passes.
RUBY_HIGH_HALL_PASS_50_CENTS 1499 Price for 50 Hall Passes.
RUBY_HIGH_HALL_PASS_100_CENTS 2499 Price for 100 Hall Passes.
RUBY_HIGH_SOLANA_RPC_URL https://api.mainnet-beta.solana.com Solana JSON-RPC endpoint used to verify token-transfer signatures for crypto pack purchases.
RUBY_HIGH_SOLANA_NFT_RPC_URL RUBY_HIGH_SOLANA_RPC_URL Optional separate RPC endpoint for NFT minting.
RUBY_HIGH_SOLANA_NFT_AUTHORITY_SECRET_KEY Server mint authority secret key for Metaplex Core pack and card NFTs. Also drives creator attribution in served JSON metadata. Set via secrets only.
RUBY_HIGH_NFT_METADATA_STORAGE Optional durable metadata JSON upload mode. Set to arweave for direct AR uploads; unset keeps app-hosted metadata JSON.
RUBY_HIGH_NFT_METADATA_ARWEAVE_JWK Arweave RSA JWK JSON used when RUBY_HIGH_NFT_METADATA_STORAGE=arweave. RUBY_HIGH_ARWEAVE_JWK, RUBY_HIGH_ARWEAVE_WALLET_JWK, and ARWEAVE_JWK are also accepted.
RUBY_HIGH_NFT_METADATA_GATEWAY https://arweave.net Gateway prefix returned for uploaded metadata JSON.
RUBY_HIGH_PACK_REVEAL_SECRET RUBY_HIGH_SOLANA_NFT_AUTHORITY_SECRET_KEY Server-only HMAC secret for deterministic pack-to-card mapping. Set a stable production secret so the mapping remains fair and non-public.
RUBY_HIGH_SOLANA_CORE_COLLECTION_ADDRESS Metaplex Core collection address for Ruby High pack NFTs. Create once with npm run nft:create-core-collection, then set this value.
RUBY_HIGH_SOLANA_CORE_CARD_COLLECTION_ADDRESS Metaplex Core collection address for Ruby High: First Bell card NFTs. Create once with npm run nft:create-card-collection, then set this value.
RUBY_HIGH_SOLANA_MEMECOIN_MINT ABHQGzXNoRbJ1sjUsCJ2TmTAo1uMx4EUpV1qYiSVpump SPL-token mint accepted for crypto pack purchases.
RUBY_HIGH_SOLANA_TREASURY_OWNER 1cfpmRU4oriteHQ9vPEN1GGuvTGuHiuX7MQCotKnHxY Treasury wallet owner that must receive the SPL-token transfer.
RUBY_HIGH_SOLANA_MEMECOIN_SYMBOL RUBY Display symbol for the Solana token.
RUBY_HIGH_SOLANA_MEMECOIN_DECIMALS 6 SPL-token decimal places used when converting quoted token amounts to base units.
RUBY_HIGH_SOLANA_HALL_PASS_5_TOKENS 1000000 $RUBY price for the 1-pack / 5 Hall Pass tier.
RUBY_HIGH_SOLANA_HALL_PASS_20_TOKENS 2800000 $RUBY price for the 3-pack / 20 Hall Pass tier (~7% volume discount vs 1-pack).
RUBY_HIGH_SOLANA_HALL_PASS_50_TOKENS 4500000 $RUBY price for the 5-pack / 50 Hall Pass tier (~10% volume discount vs 1-pack).
RUBY_HIGH_SOLANA_HALL_PASS_100_TOKENS 8500000 $RUBY price for the 10-pack / 100 Hall Pass tier (~15% volume discount vs 1-pack).
RUBY_HIGH_HOSTED_AI_HALL_PASS_COST 1 Hall Pass cost to activate server-hosted text AI for one timed window.
RUBY_HIGH_HOSTED_AI_DURATION_HOURS 168 Hosted AI Access duration. Ignored when RUBY_HIGH_HOSTED_AI_DURATION_MS is set.
RUBY_HIGH_HOSTED_AI_DURATION_MS Optional exact hosted AI pass duration override.
RUBY_HIGH_QUESTION_GENERATION_HALL_PASS_COST 1 Hall Pass cost for server-hosted Generate More Questions when the browser has no OpenRouter key.
RUBY_HIGH_MORE_QUESTIONS_COUNT 6 Default number of cards requested by Generate More Questions.
RUBY_HIGH_PORTRAIT_HALL_PASS_COST 1 Hall Pass cost for server-hosted custom portraits.
RUBY_HIGH_DIPLOMA_HALL_PASS_COST 3 Hall Pass cost for server-hosted diploma images.
RUBY_HIGH_HOSTED_IMAGE_PENDING_TTL_MS 900000 Timeout before a stuck pending hosted-image charge is failed and refunded.
RUBY_HIGH_COURSE_SLOT_HALL_PASS_COST 3 Hall Pass cost to reserve/publish one creator course slot. The legacy RUBY_HIGH_COURSE_GENERATION_HALL_PASS_COST is still honored as a fallback.
RUBY_HIGH_REVENUECAT_WEBHOOK_AUTH Required Authorization header value for /api/apps/ruby-high/billing/revenuecat/webhook. The route accepts either this exact value or Bearer <value>.
RUBY_HIGH_REVENUECAT_VIRTUAL_CURRENCY_CODE HLP RevenueCat Virtual Currency code to credit as Hall Passes when using RevenueCat Virtual Currency events.
RUBY_HIGH_CREATOR_DEFAULT_MODEL google/gemini-3.5-flash Default OpenRouter model for local teacher drafts created in Edit Pack.
RUBY_HIGH_DRAFT_GENERATIONS_PER_DAY 5 Per-teacher daily cap for draft question/course generation.
RUBY_HIGH_COURSE_GENERATION_QUESTION_COUNT 18 Default number of questions requested by AI course generation, clamped to 4–24.
RUBY_HIGH_ALLOW_HTTP_MATERIAL_URLS Set to true only in trusted local/dev environments. Remote course-material imports require HTTPS by default and reject localhost/private/reserved hosts.
RUBY_HIGH_ALLOWED_MATERIAL_HOSTS raw.githubusercontent.com,gist.githubusercontent.com Comma-separated list of additional trusted hosts for remote course-material imports. GitHub blob URLs are normalized to raw.githubusercontent.com.
RUBY_HIGH_EVAL_MODEL openai/gpt-4.1-mini LLM-judge model for npm run eval:voice when an OpenRouter key is available.
RUBY_HIGH_EVAL_REQUIRE_API Set to 1 to make npm run eval:voice fail when no RUBY_HIGH_OPENROUTER_API_KEY is configured.
The /health route is readiness: it returns 200 only after services have booted, so the platform should not route first-load traffic while Ruby High is hydrating. /livez is a process-liveness probe. The server trusts x-forwarded-* headers from the first hop for proto, host, and client IP.

No OpenRouter key is required on the server for normal play: each user can authenticate with their own key via PKCE, or use a Privy account for persistent identity/wallet ownership when Privy is configured. RUBY_HIGH_OPENROUTER_API_KEY enables hosted text AI only for sessions that spend a Hall Pass on AI Access, and enables hosted image generation with per-image Hall Pass costs. Edit Pack creates OpenRouter-backed local teacher drafts; Ruby High does not list, import, grant, or call external avatar/agent backends.

Billing and Hall Passes

Ruby High now has two currencies:

  • Merit Stars are earned by play and mirror the visible session-score payout.
  • Hall Passes are paid/entitlement currency for hosted AI windows, creator course slots, extra student slots, and hosted image generation.

Web purchases use Stripe Checkout for Hall Passes only:

  • GET /api/apps/ruby-high/billing/products returns Hall Pass top-ups, AI Access cost/duration, hosted image costs, and the separate Solana pack-NFT quote surface.
  • POST /api/apps/ruby-high/billing/ai-pass spends Hall Passes to activate server-hosted text AI for the signed-in Ruby High cookie session. A second call while active returns the existing expiry and does not spend again.
  • Publishing a draft course reserves a creator course slot for 3 Hall Passes. BYOK/local course generation does not spend Hall Passes.
  • Generate More Questions is free with browser OpenRouter or local LLM access; when it uses the server-hosted OpenRouter key, it spends 1 Hall Pass per run.
  • Unlocking an extra student slot costs 1 Hall Pass and grants a Photo Day credit; hosted character portraits consume that credit before spending a Hall Pass.
  • POST /api/apps/ruby-high/billing/checkout creates a Stripe Checkout Session for Hall Passes for the signed-in Ruby High cookie session.
  • POST /api/apps/ruby-high/billing/stripe/webhook verifies Stripe signatures and grants Hall Passes idempotently from Checkout metadata. Stripe does not sell card packs or NFTs.
  • POST /api/apps/ruby-high/billing/card-burn verifies owner-signed card burns and credits 5 Hall Passes per burned card. Hosted features then spend Hall Passes normally.

Stripe webhook events to send: checkout.session.completed and, if using asynchronous payment methods, checkout.session.async_payment_succeeded.

Solana purchases are separate from Stripe and use the configured SPL token to mint a Metaplex Core pack NFT:

  • The default token is $RUBY mint ABHQGzXNoRbJ1sjUsCJ2TmTAo1uMx4EUpV1qYiSVpump.
  • The default treasury wallet is 1cfpmRU4oriteHQ9vPEN1GGuvTGuHiuX7MQCotKnHxY.
  • Every built-in pack defaults to 100000 $RUBY.
  • Create the Core collection once with npm run nft:create-core-collection, then set RUBY_HIGH_SOLANA_CORE_COLLECTION_ADDRESS to the printed address.
  • Create the Core card collection once with npm run nft:create-card-collection, then set RUBY_HIGH_SOLANA_CORE_CARD_COLLECTION_ADDRESS to the printed address.
  • The current First Bell runtime manifest has 24 mintable profiles and a 12-profile alternate-art expansion, for 36 draft profiles total in src/services/hall-pass-card-catalog.ts. Revealed metadata includes Set, Set Code, Set Number, Card Profile ID, Card Name, Subject, media traits, and creator attribution. Reveal proof data stays under properties.provenance instead of visible marketplace traits.
  • Wallet-facing card crops are plain images, not cards inside cards: students, teachers, and specials are tall avatar crops; items are square; locations are wide. Regenerate crops with npm run nft:crop-cards after changing source art. Use node scripts/generate-nft-grok-art.mjs --parallel 3 --ids <ids> to refresh Grok source art through OpenRouter before cropping.
  • Opening a pack marks the Core pack as opened, switches its metadata to opened artwork, and creates deterministic face-down card slots. Pack/card records and receipts carry packRevealVersion, catalogHash, commitment, entropySource, and reveal-time revealSeed provenance; see docs/nft/NFT_PROVABLY_FAIR_V1_1.md for the published algorithm.
  • The current v1.1 entropy source is an auditable server-commit bridge, not decentralized randomness. The next hardening step is a Solana pack-opening program that commits the open request and payment/authority lock first, then settles from Switchboard randomness.
  • Marketplace submission copy, collection addresses, and Magic Eden verification steps are tracked in docs/nft/NFT_MARKETPLACE_VERIFICATION.md.
  • To mint with durable JSON directly on Arweave, fund the Arweave wallet, set RUBY_HIGH_NFT_METADATA_STORAGE=arweave, add the JWK secret, and verify a fresh pack/card mint returns an https://arweave.net/... metadata URI. Leave the flag unset if durable storage funding is not ready.
  • Wallet-signed pack checkout is atomic: the prepared transaction creates the treasury ATA if needed, transfers the configured SPL token with the Ruby High payment reference, and creates the Metaplex Core pack NFT. The connected wallet is the fee payer, and Ruby High co-signs only the pack authority/asset parts.
  • Each face-down card is minted and revealed one at a time by the Ruby High mint authority to the connected wallet. The connected wallet is the fee payer and recipient; Ruby High co-signs only the mint authority and deterministic card mint.
  • Owner-signed card burns are prepared one card per wallet prompt and preflighted before signing; POST /api/apps/ruby-high/billing/card-burn verifies the burn signature and credits 5 Hall Passes per burned card.
  • POST /api/apps/ruby-high/billing/solana/quote accepts the connected owner wallet and returns the treasury wallet, mint, per-session payment reference, token amount, payment-only transaction, and Solana Pay URL for a selected pack.
  • POST /api/apps/ruby-high/billing/solana/confirm accepts the signed payment transaction signature and owner wallet, then verifies the payment reference/token receipt before minting and recording the pack idempotently.

Native billing is not wired in the current public-web build. If iOS or Android comes back, do not use Stripe for digital in-app currency; create matching consumable in-app purchase products in App Store Connect and Google Play Console, validate receipts/purchase tokens server-side, then call the same Hall Pass grant path. RevenueCat can replace most receipt-validation boilerplate; the Ruby High server remains the authority that credits the wallet after validation.

RevenueCat setup:

  • Use one Offering for the shop, for example hall_passes, with consumable packages for hall_pass_5, hall_pass_20, hall_pass_50, and hall_pass_100. Product IDs with app prefixes are okay if they end in those IDs.
  • Set the RevenueCat app user ID to the Ruby High state key (rh:user:<userId>). If the app sends just <userId>, the server prefixes it to rh:user:<userId>. Anonymous RevenueCat IDs are ignored for wallet fulfillment.
  • Add a webhook pointing at /api/apps/ruby-high/billing/revenuecat/webhook and set its Authorization header to Bearer <RUBY_HIGH_REVENUECAT_WEBHOOK_AUTH> or exactly the configured value.
  • Send NON_RENEWING_PURCHASE events to grant Hall Passes and CANCELLATION events to revoke refunded Hall Passes. Refund events only debit a wallet when they match a previously recorded RevenueCat transaction; refund-first events are marked so a delayed purchase webhook for the same transaction cannot credit a refunded purchase. If using RevenueCat Virtual Currency, send VIRTUAL_CURRENCY_TRANSACTION events and set the currency code to HLP or configure RUBY_HIGH_REVENUECAT_VIRTUAL_CURRENCY_CODE.

Tests

npm test
npm run check:full
npm run test:browser
npm run eval:voice

check:full runs typecheck, the Vitest suite, and the offline SPA build. test:browser is the opt-in Playwright smoke target; it builds and launches the dev server, boots the viewer in Chromium, exercises guest play, account tabs, responsive framing, and the Privy bundle load path. eval:voice builds the package and runs the faculty-voice smoke harness; without an OpenRouter key it still verifies the local reference set and exits successfully unless RUBY_HIGH_EVAL_REQUIRE_API=1.

The suite covers the daily-class progression mechanic, the cohort, mentor mode, advantage roll, the phase machine, opinion grading, the chat layer, both store backends, the rate limiter, source-card distractor generation, pack routes, yearbook/admin routes, and the content-pack registry.

Deploy

The current production deploy is Fly.io, driven locally by npm run deploy:

npm run deploy

The App Runner workflow is retained as a legacy manual fallback only. The container itself is host-agnostic — anywhere that speaks Docker, sets PORT, and populates x-forwarded-* works.

For the IAM trust policies, the DynamoDB bootstrap, and the manual deploy fallback, see infra/README.md.

License

MIT for the code. The mechanics layer is CC BY 4.0 — see DESIGN.md §6 and §12.

About

Ruby High — eliza-app. Educational quiz/RPG where AI students compete against the player. Teachers drive the lesson; opinions are LLM-graded.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors