Skip to content

Latest commit

Β 

History

939 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Superteam Academy

A Solana-native learning platform with on-chain credentials.

Learners enroll in courses, complete lessons, and earn credentials that live on-chain:
soulbound XP as Token-2022, course certificates as Metaplex Core NFTs.

Built by Superteam Brazil β€’ MIT β€’ currently running on devnet

Architecture β€’ Features β€’ Tech Stack β€’ Quickstart β€’ Environment β€’ Docs


How it fits together

Four moving parts, each with a clear job:

Part What it is Where
Web app Next.js 15 App Router on Vercel apps/web/
On-chain program Pinocchio (Rust), 18 instructions, 6 PDA types onchain-academy/
Database Supabase Postgres β€” 36 tables, RLS on all of them supabase/
Course content A committed bundle, compiled from a separate repo solanabr/academy-courses
                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   learner's wallet ──│  Browser                     β”‚
   (SIWS / embedded)  β”‚  React Β· Monaco Β· QuickJS    β”‚
                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                      β”‚
                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                      β”‚  Next.js 15 (Vercel)         β”‚
                      β”‚  server components           β”‚
                      β”‚  72 API routes               β”‚
                      β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
                      β”‚  β”‚ committed content      β”‚  β”‚
                      β”‚  β”‚ bundle β€” no CMS        β”‚  β”‚
                      β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
                      β””β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”˜
                          β”‚          β”‚           β”‚
                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”  β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”  β”Œβ”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                 β”‚ Supabase  β”‚  β”‚  Solana  β”‚  β”‚ Build serverβ”‚
                 β”‚ DB + Auth β”‚  β”‚  devnet  β”‚  β”‚  Cloud Run  β”‚
                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β–²             β”‚
                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    Helius webhook replays
                    on-chain events into the mirror

Two invariants shape everything:

  • On-chain is the source of truth for XP balances, lesson completion (a bitmap in the Enrollment PDA), and credentials. Supabase mirrors it for fast queries, streaks, and leaderboards; mirror writes are non-fatal and rebuildable.
  • There is no CMS. Content is authored in git, compiled ahead of time by compile-content.ts, and committed to this repo as typed JSON pinned to one academy-courses commit (apps/web/content.lock). Nothing fetches content at runtime, and no credential in the app can mutate it β€” publishing is a pull request.

What learners get

On-chain credentials

  • Soulbound XP β€” Token-2022 with NonTransferable + PermanentDelegate. Minted by CPI on lesson completion; cannot be transferred or self-burned.
  • Credential NFTs β€” Metaplex Core, frozen to the learner's wallet via PermanentFreezeDelegate, one collection per course track.
  • On-chain lesson tracking β€” a 256-bit bitmap in the Enrollment PDA, one bit per lesson slot.

Interactive learning

  • A lesson is an ordered blocks[] page builder: prose, video, Monaco code challenges, quizzes, reflections, and Solana widgets (devnet airdrop, IDL program explorer, deployed-program card).
  • buildable Rust/Anchor challenges and in-browser program deploys compile through a Rust/Axum build server on Cloud Run.
  • An AI lesson assistant (Gemini-backed) with per-day spend caps enforced by a Postgres ledger.

Gamification

  • 18 achievements β€” a course-agnostic ladder plus one course badge β€” with unlock rules declared in content, not code.
  • 5 daily/multi-day quests, streaks with freezes, leagues, referrals, and a leaderboard (all-time, weekly, monthly, cohort).
  • Level is floor(sqrt(totalXP / 100)). Reward popups are queued and capped at three cards per moment.

Community and platform

  • Forum threads, answers, voting, accepted answers, a flag queue, and XP for participation with a daily cap.
  • Three languages: English, Portuguese (pt-BR), Spanish.
  • Dark/light mode on the "ink" visual system.
  • An admin console for publishing content pins, deploying courses on-chain, moderation, and insights.

Tech stack

Layer Technology
Frontend Next.js 15 (App Router), React 18, Tailwind CSS, shadcn/ui + Radix UI
Content Committed bundle compiled from the academy-courses git repo
Database / Auth Supabase (Postgres, RLS, Auth)
On-chain program Solana, Pinocchio 0.11 (Rust, cargo build-sbf)
XP tokens Token-2022 (NonTransferable + PermanentDelegate)
Credential NFTs Metaplex Core (soulbound via PermanentFreezeDelegate)
Auth SIWS + Dynamic embedded wallets + Supabase OAuth (Google, GitHub)
i18n next-intl (EN, PT-BR, ES)
Code editor Monaco Editor, QuickJS sandbox for the challenge runner
Build server Rust/Axum on GCP Cloud Run
AI assistant Gemini over raw REST, spend-metered in Postgres
RPC / indexing Helius (DAS API, webhooks)
Storage Arweave via Irys (permanent credential metadata)
Analytics GA4, PostHog, Sentry (all optional)
Monorepo Turborepo + pnpm 10
Deployment Vercel (web) + GCP Cloud Run (build server)

Getting it running

Prerequisites

For on-chain work you also need Rust, the Solana CLI, and cargo build-sbf. No Anchor CLI β€” the program is Pinocchio.

Quickstart

# 1. Clone and install
git clone https://github.com/solanabr/superteam-academy.git
cd superteam-academy
pnpm install

# 2. Configure the environment
cp .env.example apps/web/.env.local
# .env.example holds illustrative defaults, not working credentials β€” replace
# every placeholder. Minimum to boot: NEXT_PUBLIC_SUPABASE_URL,
# NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY,
# NEXT_PUBLIC_SOLANA_RPC_URL, SOLANA_RPC_URL.

# 3. Set up the database β€” migrations are the source of truth
supabase link --project-ref <your-project-ref>
supabase db push        # applies supabase/migrations/ in order
# supabase/schema.sql is a generated snapshot for diffing β€” never run it directly.

# 4. Content β€” nothing to import.
# The bundle is already committed at apps/web/src/content/generated/.
# To rebuild it after bumping the pin:
#   pnpm --filter web compile-content

# 5. Start the dev server
pnpm dev

Open http://localhost:3000.

Nothing shows up on a fresh project. Course visibility is gated on the Supabase onchain_deployments table (status = "synced" and active), which starts empty. Deploy courses from /en/admin β€” see docs/ADMIN.md.

Full on-chain features additionally need NEXT_PUBLIC_PROGRAM_ID, NEXT_PUBLIC_XP_MINT_ADDRESS, PROGRAM_AUTHORITY_SECRET, and BACKEND_SIGNER_SECRET β€” see docs/DEPLOY-PROGRAM.md for the deploy-and-initialize workflow.

Commands

pnpm dev          # Next.js dev server
pnpm build        # production build (Turborepo)
pnpm lint         # ESLint
pnpm typecheck    # tsc
pnpm format       # Prettier

Repository map

superteam-academy/
β”œβ”€β”€ apps/
β”‚   β”œβ”€β”€ web/                      # Next.js 15 app
β”‚   β”‚   β”œβ”€β”€ src/app/[locale]/     #   pages β€” (marketing), (platform), admin
β”‚   β”‚   β”œβ”€β”€ src/app/api/          #   72 API routes
β”‚   β”‚   β”œβ”€β”€ src/lib/              #   content, solana, auth, gamification, ai, helius…
β”‚   β”‚   β”œβ”€β”€ src/content/generated/#   COMMITTED content bundle β€” never hand-edit
β”‚   β”‚   β”œβ”€β”€ src/messages/         #   i18n catalogs (en, pt-BR, es)
β”‚   β”‚   β”œβ”€β”€ scripts/              #   compile-content.ts (content repo β†’ bundle)
β”‚   β”‚   └── content.lock          #   the academy-courses commit the bundle is pinned to
β”‚   └── build-server/             # Rust/Axum program compiler (Cloud Run)
β”œβ”€β”€ onchain-academy/              # Pinocchio program workspace + IDL + tests
β”œβ”€β”€ packages/
β”‚   β”œβ”€β”€ types/                    # shared TypeScript interfaces
β”‚   β”œβ”€β”€ content-schema/           # Zod schemas for the content standard
β”‚   β”œβ”€β”€ content-lint/             # content linter β€” runs in academy-courses CI
β”‚   β”œβ”€β”€ challenge-executor/       # sandboxed challenge runner (QuickJS)
β”‚   β”œβ”€β”€ deploy/                   # browser-side Solana program deployment
β”‚   └── config/                   # shared ESLint / TS / Tailwind configs
β”œβ”€β”€ supabase/                     # migrations (source of truth) + schema snapshot
β”œβ”€β”€ scripts/                      # operator scripts
β”œβ”€β”€ wallets/                      # keypairs (gitignored)
└── docs/                         # the guides listed below

Course content lives in a separate repo: solanabr/academy-courses.

Environment variables

Copy .env.example to apps/web/.env.local and fill in values. Required variables are validated at boot β€” a missing one fails loudly rather than degrading silently.

The authoritative, annotated list β€” every variable, what it does, and what happens when it is unset β€” is the ## Environment Variables block in apps/web/CLAUDE.md. The tables below are the shape of it, not a replacement.

Required

Variable Scope Description
NEXT_PUBLIC_SUPABASE_URL Client Supabase project URL
NEXT_PUBLIC_SUPABASE_ANON_KEY Client Public anon key (safe for the browser)
SUPABASE_SERVICE_ROLE_KEY Server Service role key. Never exposed to the browser; also gates admin auth
NEXT_PUBLIC_SOLANA_RPC_URL Client Browser RPC. Must carry no privileged key, or a domain-restricted Helius key
SOLANA_RPC_URL Server Server RPC β€” this is the one that may carry an unrestricted Helius key
NEXT_PUBLIC_APP_URL Client Base URL for sitemap, OG tags, and NFT metadata URIs. A production build fails without it

On-chain features

Variable Scope Description
NEXT_PUBLIC_PROGRAM_ID Client Deployed program id
NEXT_PUBLIC_XP_MINT_ADDRESS Client XP mint pubkey, from the initialize output
NEXT_PUBLIC_SOLANA_NETWORK Client Network name (devnet)
PROGRAM_AUTHORITY_SECRET Server Authority keypair (JSON array of 64 bytes) β€” the one that signed initialize
BACKEND_SIGNER_SECRET Server Rotatable backend co-signer. On devnet, the same as the authority
XP_MINT_AUTHORITY_SECRET Server XP mint authority. Omit to disable XP minting

There is no admin password. Admin access is the caller's Supabase session checked against the admin_users allowlist; the old ADMIN_SECRET is retired.

Optional β€” each one switches a feature on

Set these for… Variables
Helius event ingestion HELIUS_API_KEY, HELIUS_WEBHOOK_SECRET
The admin publish card GITHUB_TOKEN β€” read scope only; unset β‡’ that card 503s
Rust builds and deploys BUILD_SERVER_URL, BUILD_SERVER_API_KEY, RUST_PLAYGROUND_URL
The AI lesson assistant GEMINI_API_KEY, AI_PARTNER_SEAL_SECRET, the AI_SPEND_* caps, the AI_MODEL_* overrides
Embedded wallets NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID
Permanent credentials ARWEAVE_UPLOADER_SECRET β€” a Solana keypair funding Irys, not an Arweave JWK
Outbound email + cron RESEND_API_KEY, EMAIL_FROM, CRON_SECRET β€” fail-closed: unset means nothing is sent
Moderation notifications MODERATION_WEBHOOK_URL
Teacher course preview TEACH_PREVIEW_PASSWORD β€” unset disables the preview
Analytics NEXT_PUBLIC_GA4_MEASUREMENT_ID, NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_HOST, NEXT_PUBLIC_SENTRY_DSN

NEXT_PUBLIC_* values are inlined at build time. Changing one requires a redeploy with "Use existing Build Cache" disabled β€” a cache-reusing redeploy keeps the old value baked into the served chunks.

Signing in

Three ways in, all of which end at a server-set Supabase cookie session β€” nothing mints a session client-side:

Rail What proves the user Exchanged at Optional?
SIWS, external wallet The wallet signs a server-issued nonce /api/auth/wallet No β€” the guaranteed path and the kill switch
Dynamic embedded wallets Dynamic proves the Google handshake + wallet ownership /api/auth/dynamic Yes β€” unset NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID
Supabase OAuth Google or GitHub, through Supabase Auth /api/auth/callback Yes β€” the fallback rail

The full map, including the post-login rituals every rail must share, is docs/AUTH-FLOWS.md.

On-chain program

Built with Pinocchio 0.11 (#![no_std], cargo build-sbf). The Anchor implementation it was ported from has been deleted β€” there is no Anchor.toml and no Anchor CLI in the toolchain. The committed IDL at onchain-academy/idl/onchain_academy.json is still Anchor IDL format, because that is the wire contract every client decodes against.

Surface Count Detail
Instructions 18 dispatched on the 8-byte sha256("global:<name>") sighash
PDA types 6 Config, Course, Enrollment, MinterRole, AchievementType, AchievementReceipt
Errors 37 6000+, mapped back to i18n keys client-side
Events 18 emitted byte-identically to Anchor's emit!, so existing indexers keep working

The instruction set covers the whole learning lifecycle: initialize and config, course CRUD, enroll and close enrollment, complete lesson, finalize course, issue and upgrade credential, register/update/revoke minter, reward XP, and the achievement type/award pair.

The program id is baked at compile time in two flavors. The default build carries the upstream id 7NeJaSRyb4Wxay3Tcd9bdpD7T3GWYUQSFyrhG8SgwE8V (what the IDL declares); --features fresh-id carries Dsro2Cd9Mhgk8L71imh3LLPwYU5PU8hvBY5HEcPrcx5u, the self-owned devnet instance that is actually deployed and initialized. Clients read the id from NEXT_PUBLIC_PROGRAM_ID and throw if it is unset.

Byte-level layouts, validation order, and every invariant: docs/SPEC.md. Deploying your own instance: docs/DEPLOY-PROGRAM.md.

Admin console

URL: /{locale}/admin (e.g. /en/admin) Auth: your ordinary Supabase session, checked against an admin_users allowlist that only the service role can read. There is no admin password and no login form β€” signed-in non-admins get a 404.

Screen What it does
Courses The content pin vs academy-courses HEAD, a prefilled publish PR link, on-chain course/achievement deploys, and activate/deactivate
Moderation The pending community-flag queue
Insights Platform-behaviour aggregates β€” AI-tutor usage, spend, lesson funnel
Status Program liveness, authority match, deploy counts, and on-chain β†’ Supabase resync

Publishing is a pull request β€” the console holds no write token and cannot mutate content. Deploys are recorded in onchain_deployments, which is the learner-visibility gate. See docs/ADMIN.md.

Documentation

Doc What it covers
ARCHITECTURE.md System design, data flows, service interfaces, DB schema
AUTH-FLOWS.md Every way into a session, mapped from the code
SPEC.md Authoritative on-chain program specification
DEPLOYMENT.md Vercel, Supabase, Cloud Run, cron, analytics
DEPLOY-PROGRAM.md Build, deploy, and initialize the program on devnet
ADMIN.md The admin console, screen by screen
CUSTOMIZATION.md Theming, i18n, extending content and gamification
PINOCCHIO-MIGRATION.md What changed in the Anchor β†’ Pinocchio port
DB-MIGRATION-LEDGER.md Filename ↔ prod migration-ledger reconciliation
CLAUDE.md Codebase conventions, security model, env-var reference

Contributing

Branch, commit conventionally, open a PR:

git checkout -b feat/your-thing
git commit -m "feat: add quiz lesson type"

House rules that CI actually enforces:

  • TypeScript strict, zero any
  • All UI strings through next-intl β€” never hardcoded in components
  • Server components by default; client components only where needed
  • ESLint + Prettier on a pre-commit hook
  • A byte-comparison of the committed content bundle against a fresh recompile

License

MIT.

Thanks to Superteam Brazil and the Solana Foundation.

Releases

Packages

Contributors

Languages