Unified platform hub for carbon-neutral trading — Pokémon collectibles (PokePocket), carbon credits (CO2T.earth), and digital entertainment storefronts on one TSI stack. Prove data flows in Platform operations, exercise APIs in Test lab, and follow Railway setup in Setup walkthrough.
Unified platform surfaces
One main-api, shared Dynamic + Supabase auth, supplier-scoped Postgres. PokePocket is the live reference; earth and CO2True follow the same BFF + checkout patterns.
pokepocket · pokepocket.comAuthenticated collectible cards with carbon-neutral checkout via Biochar — powered by CO2T.earth
co2trust · co2t.earthCarbon credits, biochar suppliers, and marketplace listings on the same TSI stack
Repo co2t-ts-co2t-earth-site — deploy on TSI dev per hub MAIN-DEPLOYMENT-READINESS ↗
co2true · co2true.earthMethodology education and entertainment storefronts — shared auth and checkout patterns
Repo co2t-ts-co2true-site — deploy on TSI dev per hub MAIN-DEPLOYMENT-READINESS ↗
Tier A — Shared environment (non-secret)
- ALLOW_ORIGINS
Comma-separated storefront + console origins for CORS on main-api
- SUBGRAPH_URL
Goldsky subgraph endpoint for credit/retirement sync
- Internal DNS
rabbitmq.railway.internal, postgres refs — never copy from legacy verbatim
- Storefront partition keys
NEXT_PUBLIC_STOREFRONT_ID, NEXT_PUBLIC_STOREFRONT_SUPPLIER_ID (e.g. pokepocket)
cd co2t-tracking-system cp env/tsi.dev.shared.env.example env/tsi.dev.shared.env # paste into Railway → dev → Shared Variables
Tier B — Per-service infrastructure
- main-api
FastAPI — products, orders, suppliers, payments routers
- Postgres
TSI-only database — one per Railway environment
- RabbitMQ
Sync jobs (orders, NFT, balances) — internal AMQP URL
- stripe-webhook
Stripe events → main-api (optional for checkout)
- api-websockets
Realtime fan-out via RabbitMQ (optional)
Tier C — Secrets (never in UI or client bundles)
- BACKEND_SECRET_KEY
Must match on main-api and your storefront BFF
- DATABASE_URL
Postgres — TSI only, never legacy
- RABBITMQ_URL
Internal Railway reference per service
- DYNAMIC_PUBLIC_KEY
RS256 SPKI for wallet JWT validation (server only)
- SUPABASE_SERVICE_ROLE_API_KEY
User upsert — server only
- STRIPE_* / PRICECHARTING_API_TOKEN
Payments and import tooling as needed
Auth pattern — Dynamic + Supabase + main-api JWT
- 1. Dynamic widget
User connects ZeroDev wallet in your Next.js storefront
- 2. BFF validates JWT
Server validates Dynamic token with DYNAMIC_PUBLIC_KEY
- 3. Supabase upsert
users + users_to_dynamic rows (legacy-compatible model)
- 4. main-api session
POST /co2trust-services/v1/users/auth with BACKEND_SECRET_KEY → bearer JWT
- 5. httpOnly cookie
Store JWT in httpOnly cookie — never expose BACKEND_SECRET_KEY to browser
CORS / ALLOW_ORIGINS
- Register every origin
Storefront URL, deploy-console (https://tsi-console-dev.co2t.earth), local dev ports if needed
- Verify from browser
Catalog fetch from storefront must not fail CORS preflight
ALLOW_ORIGINS=https://tsi-console-dev.co2t.earth,https://pokepocket-site-dev.up.railway.app,https://your-partner-app.up.railway.app
Storefront template — pokepocket as reference
- Supplier-scoped BFF
GET /api/products/storefront?supplier_id=… — never unscoped public catalog
- Metadata stamping
storefront, catalog_category, source fields on product_metadata
- co2t-ts-frontend-basic
Shared apiClient, apiTypes, storefront helpers
- Deploy on Railway
Next.js service in co2t-tsi project with NEXT_PUBLIC_API_V1_URL → main-api
NEXT_PUBLIC_API_V1_URL=https://<main-api-dev-domain>
NEXT_PUBLIC_STOREFRONT_SUPPLIER_ID=your-supplier-id NEXT_PUBLIC_STOREFRONT_ID=your-brand
What NOT to copy from legacy
- Legacy DATABASE_URL / RABBITMQ_URL
TSI Postgres and AMQP are separate infrastructure
- main-api-prd internal hosts
Use TSI public Railway URLs or internal *.railway.internal for same project
- Shared DB assumption
Each environment has its own empty-then-seeded TSI database
- NextAuth-only flows
TSI storefronts use Dynamic + httpOnly JWT cookie BFF (see pokepocket-site)
- Unscoped admin routes in public pages
Filter by supplier_id + metadata in BFF before hitting main-api
Reference storefront — pokepocket (copy patterns)
- Catalog + PDP
BFF filters by storefront metadata — not supplier_id alone
- Admin sidebar
/admin, /admin/products, /admin/listings, live PriceCharting import, remediation
- Account hub
/account, /account/collection, /account/orders, /account/settings — live Phase 3
- Checkout /checkout
Cart + Stripe session BFF — e2e blocked until STRIPE_SECRET_KEY on Railway
- Messages /messages
Supabase DM panel — same auth stack as console Test lab
- Env parity
Dynamic + Supabase vars synced via hub script (--target-service)
cd co2t-tracking-system ./scripts/railway-sync-storefront-to-console.sh --target-service pokepocket-site --dry-run # then --apply --yes when ready
Launch verification
- Platform ops shows your supplier
Entity counts + supplier row in /operations
- Test lab proxy returns JSON
Platform data explorer with signed-in JWT
- stack-smoke green
Hub script against your MAIN_API_URL
- CORS checkout path
Create product-order from your storefront (when Stripe ready)
STACK_URL=https://<main-api-dev-domain> ./co2t-tracking-system/scripts/stack-smoke.sh