Javis OS
Javis OS is an Agentic AI that assists entrepreneurs in business tasks, helping them grow their work.
AI translation
Supercharge audience engagement with real time Q&A at scale. Audience submits their questions throughout the presentation, top questions rise to the top through audience votes, and a moderator manages
BusinessTypeScript · CSSGitHub @meerkat-eventsOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Audience engagement tool for questions and answers
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
The audience engagement tool for in-person and virtual conferences, used by the
Ethereum Foundation for Devcon, Devconnect, and others.
Attendees scan a QR code to ask and upvote questions and send reactions.
Organizers pick the question being answered and mark the current session as
live. A presenter view runs on the stage screen and follows the live session.
Optionally, attendees can collect a signed attendance proof (a
POD) into their Zupass.Read the full README ↗The audience engagement tool for in-person and virtual conferences, used by the Ethereum Foundation for Devcon, Devconnect, and others.
Attendees scan a QR code to ask and upvote questions and send reactions. Organizers pick the question being answered and mark the current session as live. A presenter view runs on the stage screen and follows the live session. Optionally, attendees can collect a signed attendance proof (a POD) into their Zupass.
| Path | What it is |
|---|---|
api/ | The application: Hono HTTP API, React Router 8 frontend (SSR), Drizzle schema and migrations |
packages/react/ | @meerkat-events/react — hooks to embed live questions in your own site |
scripts/setup.sh | One-shot local setup |
curl -fsSL https://get.pnpm.io/install.sh | sh -,
see pnpm installation); pnpm then switches
itself to the version pinned in packageManager. ./scripts/setup.sh does
this for youpsql, used by the seed scriptCreate api/.env from the example and fill in the values (see
Environment variables):
cp api/.env.example api/.env
Run the setup script from the repository root:
./scripts/setup.sh
It installs dependencies, builds the workspace packages, applies the
database migrations and seeds two demo conferences with events. It is safe
to re-run and leaves an existing api/.env untouched.
Configure the Supabase project. supabase/config.toml reproduces the
provider, OTP and rate-limit settings below with supabase config push;
supabase/README.md has the runbook and lists what
stays manual.
Auth → Providers: enable Email and Anonymous sign-ins. Login
uses a one-time code, so the magic-link email template must include
{{ .Token }}.
Auth → URL Configuration: set the Site URL and redirect URLs to the
app's BASE_URL; for production, also configure custom SMTP.
Auth → Rate Limits, for conference traffic: enable IP Address
Forwarding (used with SUPABASE_SECRET_KEY) and raise the per-IP
limits, since a venue's attendees share one Wi-Fi IP. Meerkat runs with
6000 token verifications per 5 minutes, 3000 token refreshes per 5
minutes and 3000 anonymous sign-ins per hour.
Row Level Security and realtime: after the first migration, apply the
Supabase-specific setup that Drizzle migrations do not cover (RLS on every
table, realtime SELECT policies, and the questions, reactions and
votes tables in the supabase_realtime publication). Reactions on the
presenter view and live updates on the management pages subscribe to
Postgres changes on these tables. The script is idempotent:
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f api/scripts/supabase-policies.sql
cd api && pnpm dev
The app is served at http://localhost:8000. Set PORT to use a different
port; the frontend follows automatically. In a new git worktree, run
./scripts/worktree-setup.sh once to copy api/.env, install dependencies
and build (it also runs automatically at the start of a Claude Code session).
pnpm dev runs the Node server with --watch and serves the built
frontend from api/build/. Backend changes reload automatically; after
changing anything under api/app/, run pnpm build in a second terminal and
the server restarts on the new bundle.
Useful commands in api/:
| Command | Purpose |
|---|---|
pnpm build | Build the frontend and SSR bundle |
pnpm typecheck | Generate route types, build, and run tsc |
pnpm lint | ESLint |
pnpm generate | Create a migration from changes in api/schema.ts |
pnpm migrate | Apply pending migrations |
The React package is built with pnpm build in packages/react/ (or
pnpm -r build from the root). The app consumes it from packages/react/dist,
so rebuild it after changing the package.
docker build -t meerkat .
docker run --rm --env-file api/.env -p 8000:8000 meerkat
VITE_API_URL is compiled into the frontend at build time; leave it unset (as
above) and the frontend calls the origin it was served from, which is what
this single container needs. Pass --build-arg VITE_API_URL=<origin> only
when the frontend and API are served from different origins.
Deploys go to Fly.io through GitHub Actions
(.github/workflows/continous-deployment.yml): a push to master deploys the
dev environment, creating a GitHub release deploys prod. fly.template.toml
is rendered with the repository secrets at deploy time.
Set in api/.env (see api/.env.example).
Required:
| Variable | Purpose |
|---|---|
DATABASE_URL | Postgres connection string. DATABASE_POOLER_URL, if set, takes precedence at runtime. |
PRIVATE_KEY | Signs attendance PODs. Generate with openssl rand -hex 32. |
SUPABASE_URL | Supabase project URL. |
SUPABASE_ANON_KEY | Supabase anon key, used by the browser for Auth and Realtime. |
SUPABASE_SERVICE_ROLE_KEY | Supabase service role key, used server-side for broadcasts and Devcon handover sign-ins. |
DEVCON_VERIFICATION_SECRET | Shared HS256 secret for verifying Devcon handover tokens (the Devcon app's VERIFICATION_SECRET). |
ZUPASS_URL | Zupass server URL. |
ZUPASS_ZAPP_NAME | Name of the Zupass zapp; also namespaces the POD collection. |
Optional:
| Variable | Purpose |
|---|---|
PORT | Listen port (default 8000). |
BASE_URL | Public origin of the app, used for redirects and QR codes. Default http://localhost:$PORT. |
VITE_API_URL | API origin compiled into the frontend. Default empty: the frontend calls the origin it was served from. |
CORS_ORIGINS | Comma-separated origins allowed to call /api/* from a browser. Default: *. |
PRETALX_URL | Origin of the Pretalx instance whose schedules are synced into conferences. Unset disables the sync endpoint. |
SUPABASE_SECRET_KEY | Supabase secret API key (sb_secret_…). When set, Devcon handover sign-ins forward the attendee's IP so Supabase rate-limits per attendee rather than per server; also enable IP Address Forwarding in the project. |
DATABASE_POOLER_URL | Connection string of a connection pooler, preferred over DATABASE_URL. |
DATABASE_MAX_POOL_SIZE | Max DB pool size (default: 10). |
SENTRY_DSN | Sentry error tracking DSN. |
ENVIRONMENT | Environment name reported to Sentry (default: development). |
OPENROUTER_API_KEY | OpenRouter key for automatic question moderation. Unset disables moderation everywhere. |
After modifying the schema in api/schema.ts:
cd api
pnpm generate # creates a migration file in api/drizzle/
pnpm migrate # applies pending migrations
Review the generated SQL before applying it: the auth.users table is managed
by Supabase, so remove any statements the generator emits for it.
Through the admin API, authenticated with an x-api-key header:
POST /api/v1/admin/conferences creates a conference.POST /api/v1/admin/events upserts a batch of events, keyed by uid.API keys are stored as argon2 hashes in the api_keys table; insert a hashed
key there to create one.
Insert a row into invitations (email, conference_id, role) before the
person signs up. A database trigger grants the role when their account is
created. For an existing user, insert directly into conference_role.
With OPENROUTER_API_KEY set, every new question of a conference with a
moderation config is classified before it is stored. Questions that look
like harassment, threats, doxxing, explicit content, self-harm, hate,
discrimination, phishing, scams or shilling, politics unrelated to crypto
(outside talks about politics) or current wars and genocide are hidden from
everyone but their author (compliments are fine); if the classifier fails, questions are shown
as before. Write a short context about the conference, check it with the eval,
then set it:
UPDATE conferences
SET moderation = '{"context": "Devcon, the Ethereum Foundation''s developer conference. Talks are in English."}'
WHERE id = <id>;
NULL switches moderation off again; UPDATE questions SET hidden_at = NULL WHERE uid = '<uid>' shows a wrongly hidden question. The design, the eval and
the setup per environment are in
designs/002-question-moderation.md.
/stage/<stage>/qa redirects to the Q&A page of the live event on that stage
(or the next upcoming one), and /stage/<stage> opens the presenter view and
keeps following the live event. Use these for signage and slides.