A pnpm monorepo for a multilingual, server-first intake screener for Spain's 2026 extraordinary regularisation process and related organisation workflows.
- guides people through a one-question-per-page journey
- stores progress in a session cookie
- shows a check-answers step before the result
- runs triage rules to produce a cautious next-step outcome
- supports assisted completion and language switching
- uses locale-prefixed URLs for every public route (
/es/...,/en/...,/fr/...,/ar/...)
mise install
mise x -- pnpm install --frozen-lockfile
mise x -- pnpm devThe default pnpm dev command runs the public app.
To run apps explicitly:
pnpm dev:public
pnpm dev:orgTo check that the analytics environment contract is satisfied:
mise run analytics:env:checkpnpm run check
pnpm run typecheck
pnpm testpnpm test loads committed .env.test for the public app so Matomo-related unit tests match CI (see apps/public/package.json test script).
pnpm build
pnpm build:public
pnpm preview- apps/public — public Primer Paso service
- apps/org-portal — authenticated organisation portal placeholder
- packages/certificate — shared certificate model and PDF generation package placeholder
- packages/auth — shared role and permission model placeholder
- packages/db — shared persistence package placeholder
- packages/config — shared app configuration placeholder
- packages/ui — shared UI package placeholder
- docs/ — design spec, journey, triage rules, ADRs
- the core flow uses standard HTML forms and SvelteKit form actions
- the app is configured for Netlify deployment
- this service is triage, not a legal determination
The public certificate handoff flow stores the certificate draft server-side and places only an opaque token in the handoff URL/QR code.
To enable it:
- apply
packages/db/migrations/001_certificate_handoffs.sqlto Postgres - set
PRIVATE_DATABASE_URL - set
PUBLIC_CERTIFICATE_HANDOFF_ENABLED=true - set
PUBLIC_ORG_PORTAL_URL
The raw token is never stored. The database stores only a SHA-256 token hash.
The organisation portal uses Supabase Auth magic links. It is not open registration:
the email must already exist in organisation_members, and the same address must
be able to sign in via your Supabase project.
For local testing, set:
PRIVATE_DATABASE_PROVIDER=supabase
PRIVATE_DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres
PUBLIC_CERTIFICATE_HANDOFF_ENABLED=true
PUBLIC_ORG_PORTAL_URL=http://localhost:5174
PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
PUBLIC_SUPABASE_PUBLISHABLE_KEY=your-local-anon-key
PRIVATE_ORG_PORTAL_CUSTOM_SMTP_CONFIGURED=falseFor local Supabase, run supabase status and copy the local anon key into
PUBLIC_SUPABASE_PUBLISHABLE_KEY. Magic links return to /auth/confirm.
For deployed environments, set PRIVATE_ORG_PORTAL_CUSTOM_SMTP_CONFIGURED=true
and use custom SMTP in the Supabase dashboard.
See docs/supabase-org-auth.md for the Supabase dashboard setup.
Reset and seed the local Supabase database:
pnpm db:resetSeed a local collaborating organisation and test users:
pnpm db:seed:local-org-portalThe seed command loads the repository-root .env automatically. If you run the
package script directly from packages/db, it loads ../../.env.
Seeded local member emails:
admin@example.invalid
volunteer@example.invalid
signer@example.invalidUse any of those emails on the organisation portal sign-in page; Supabase will send a sign-in link if the project is configured.
The organisation portal uses Supabase Auth for email magic-link sign-in, then maps
the authenticated Supabase user email to an active row in organisation_members.
Supabase authenticates the inbox. Primer Paso still owns organisation membership,
roles, and permissions.
Set:
Site URL: https://org.primerpaso.org
Redirect URL: https://org.primerpaso.org/auth/confirmAdd preview and local callback URLs as needed, for example:
http://localhost:5174/auth/confirmOnly add origins you actually use. Avoid wildcard redirects for production.
Enable email sign-ins.
Use the magic-link email flow. The application passes emailRedirectTo when it
requests the link, so links should return to:
/auth/confirmThe confirmation route exchanges the Supabase token hash for a session and then checks that the email belongs to an active organisation member.
Configure a real SMTP provider before deployed use. Do not rely on Supabase's default email service for production or previews.
Set this only after SMTP is configured and tested:
PRIVATE_ORG_PORTAL_CUSTOM_SMTP_CONFIGURED=trueFor local development against a hosted Supabase project, this can stay false:
PRIVATE_ORG_PORTAL_CUSTOM_SMTP_CONFIGURED=falseRecommended Supabase project settings for this repository:
- Enable Data API: optional. The current app does not need it for server-side database access, because it uses the Postgres connection directly.
- Automatically expose new tables and functions: disable.
- Enable automatic RLS: enable.
Even if the Data API is not used now, keeping RLS enabled by default is a useful defence against future accidental exposure.
Use Postmark for the organisation portal.
It is a better fit for this workflow because the portal sends low-volume, high-trust transactional email where deliverability and clear operational debugging matter more than marketing features. Resend is also good, especially for developer experience, but Postmark is the safer default for magic-link auth emails in a public-interest service.
Basic setup:
- Create a Postmark account.
- Add and verify the sending domain, for example
primerpaso.org. - Add the DNS records Postmark gives you for SPF, DKIM, and return-path/bounce handling.
- Create a server/message stream for transactional email.
- Copy the SMTP host, port, username, and password into Supabase: Authentication -> Emails -> SMTP Settings.
- Send a test magic link from the organisation portal.
- Set
PRIVATE_ORG_PORTAL_CUSTOM_SMTP_CONFIGURED=truein deployed environments.