This document describes the CI/CD pipeline configuration, branch protection rules, and custom runner requirements for the ILN Frontend repository.
Temporarily disabled (2026-08-10):
namespace-profile-nurscastopped picking up jobs —gh api repos/.../actions/runnersreturns zero registered runners, and every workflow that targeted it had been stuck inqueuedfor 8+ hours across multiple pushes. All workflows below were switched toruns-on: ubuntu-latestas a temporary mitigation so CI keeps running. This is a runner/infrastructure issue (likely on the Namespace.so side — expired registration, billing, or a broken webhook), not a code issue. Once the runner is confirmed healthy again, revert theseruns-onlines back tonamespace-profile-nursca(see the "Fork Contributors" example below for the exact line to change, in reverse).
All GitHub Actions workflows in this repository normally use a custom/self-hosted runner labeled namespace-profile-nursca.
namespace-profile-nursca is a self-hosted GitHub Actions runner configured specifically for this project. It provides:
- Custom environment configurations for Stellar/Soroban development
- Pre-installed dependencies and tools specific to the ILN stack
- Optimized performance for the project's specific testing requirements
The runner is configured at the organization/repository level in GitHub Actions settings. Maintainers with admin access can view and modify runner configuration in:
- Repository Settings → Actions → Runners
- Organization Settings → Actions → Runners (if configured at org level)
If namespace-profile-nursca becomes unavailable:
- For this repository: Workflows will fail until the runner is restored
- For forks: Forks will not have access to this custom runner
If you are working from a fork, you must modify workflow files to use GitHub-hosted runners:
- Change all instances of
runs-on: namespace-profile-nurscatoruns-on: ubuntu-latest - Note that some workflows may require additional configuration changes when using
ubuntu-latest - Be aware that test execution times may differ on GitHub-hosted runners
Example change:
# Before
runs-on: namespace-profile-nursca
# After (for forks)
runs-on: ubuntu-latestThe following workflows are configured to use namespace-profile-nursca under normal operation. As of 2026-08-10 they are temporarily pinned to ubuntu-latest — see the notice above.
ci.yml- Lint, unit tests, coverage, build (4 jobs)e2e-tests.yml- End-to-end Playwright testsvisual-regression.yml- Chromatic visual regression tests (thechromaticjob only;check-secretandchromatic-skip-noticealready run onubuntu-latest)lighthouse.yml- Lighthouse performance budget testsaccessibility.yml- Consolidated accessibility test suitecontract-tests.yml- Stellar SDK contract integration testsbundle-size.yml- Bundle size regression trackingfeature-flag-audit.yml- Informational feature flag report (PR-triggered only)
Note: workflow-lint.yml, mutation-testing.yml, flaky-test-detection.yml, nightly-testnet-e2e.yml, pr-issue-link-check.yml, pr-size-label.yml, and wave-points-summary.yml already use ubuntu-latest and are unaffected.
The main branch is protected with the following requirements:
Before merging to main, all of the following CI checks must pass:
- CI / lint - ESLint validation
- CI / tests - Unit test suite (Vitest)
- CI / build - Production build verification
- End-to-End Tests / e2e - Playwright E2E test suite
- Lighthouse Performance Budget / lighthouse - Performance budget validation
- Visual Regression Tests / chromatic - Chromatic visual regression checks
- Accessibility Tests / accessibility - jest-axe accessibility validation
- Contract Integration Tests / contract-tests - Stellar SDK contract tests with 90% coverage enforcement
- Require pull request reviews before merging: Yes (minimum 1 reviewer)
- Require status checks to pass before merging: Yes
- Require branches to be up to date before merging: Yes
- Do not allow bypassing the above settings: No (admins can bypass)
The develop branch is protected with the following requirements:
Before merging to develop, all of the following CI checks must pass:
- CI / lint - ESLint validation
- CI / tests - Unit test suite (Vitest)
- CI / build - Production build verification
- Lighthouse Performance Budget / lighthouse - Performance budget validation
- Visual Regression Tests / chromatic - Chromatic visual regression checks
- Accessibility Tests / accessibility - jest-axe accessibility validation
- Contract Integration Tests / contract-tests - Stellar SDK contract tests with 90% coverage enforcement
Note: E2E tests (End-to-End Tests / e2e) are not required for develop branch merges.
- Require pull request reviews before merging: Yes (minimum 1 reviewer)
- Require status checks to pass before merging: Yes
- Require branches to be up to date before merging: Yes
- Do not allow bypassing the above settings: No (admins can bypass)
Triggers:
- Push to
mainordevelop - Pull requests targeting
mainordevelop
Jobs:
lint- ESLint validationtests- Unit tests + accessibility testsbuild- Production build verification
Triggers:
- Push to
mainordevelop - Pull requests targeting
mainordevelop
Jobs:
e2e- Playwright E2E test suite with artifact uploads
Triggers:
- Push to
mainordevelop - Pull requests targeting
mainordevelop
Jobs:
chromatic- Chromatic visual regression testing (requiresCHROMATIC_PROJECT_TOKENsecret)
Triggers:
- Push to
mainordevelop - Pull requests targeting
mainordevelop
Jobs:
lighthouse- Lighthouse CI performance budget validation
Triggers:
- Push to
mainordevelop - Pull requests targeting
mainordevelop
Jobs:
accessibility- Consolidated accessibility validation for the dedicated axe suite and page-level accessibility tests
Triggers:
- Push to
mainordevelop - Pull requests targeting
mainordevelop
Jobs:
contract-tests- Stellar SDK contract tests with 90% coverage enforcement
Triggers:
- Push to
mainordevelopwith changes to.github/workflows/** - Pull requests with changes to
.github/workflows/**
Jobs:
actionlint- GitHub Actions workflow syntax validation
The following secrets must be configured in the repository settings:
CHROMATIC_PROJECT_TOKEN- Project token for Chromatic visual regression testing
GITHUB_TOKEN- Automatically provided by GitHub Actions (no manual configuration needed)
This section documents the environment variables that each workflow sets or implicitly requires. It is the authoritative reference for understanding what env context a workflow runs under, and flags potential gaps where a missing variable could cause a silent failure.
| Job | Variable | Source | Value / Note |
|---|---|---|---|
build |
(none set explicitly) | — | The build step does not set NEXT_PUBLIC_STELLAR_NETWORK or any feature flags. Next.js will use the defaults baked into src/lib/env.ts (e.g. NEXT_PUBLIC_STELLAR_NETWORK=testnet). This is intentional — the build verifies that the app compiles with fallback values only. |
Gap: If a future feature flag is added without a hardcoded default in src/lib/env.ts, the CI build may silently build with the flag disabled. Always provide a sensible default in env.ts.
| Job | Variable | Source | Value / Note |
|---|---|---|---|
lighthouse |
CI |
inline env: |
true — suppresses interactive prompts |
lighthouse |
NEXT_PUBLIC_STELLAR_NETWORK |
inline env: |
testnet — ensures Soroban RPC points to testnet during the build |
lighthouse |
LHCI_GITHUB_TOKEN |
secrets.GITHUB_TOKEN |
Used to post Lighthouse results as a PR status check |
Note: All other NEXT_PUBLIC_* variables use their defaults from src/lib/env.ts. Feature flags (NEXT_PUBLIC_NFT_ENABLED, NEXT_PUBLIC_INSURANCE_POOL_ENABLED, NEXT_PUBLIC_ORACLE_ENABLED) default to false in this workflow, meaning the Lighthouse audit runs against the baseline feature set.
| Job | Variable | Source | Value / Note |
|---|---|---|---|
e2e |
NEXT_PUBLIC_API_MOCKING |
inline env: |
"enabled" — activates MSW mock service worker so tests run without a live Soroban RPC |
Note: No NEXT_PUBLIC_STELLAR_NETWORK is set. The app uses the testnet default from src/lib/env.ts. Tests run against mocked network responses via MSW.
| Job | Variable | Source | Value / Note |
|---|---|---|---|
accessibility |
CI |
inline env: |
true — disables watch mode, ensures clean exit |
Note: No network variables are needed; accessibility tests use Vitest with jsdom and do not connect to Stellar.
| Job | Variable | Source | Value / Note |
|---|---|---|---|
contract-tests |
CI |
inline env: |
true |
Note: Contract tests mock the Stellar SDK at the module boundary via vi.mock() and do not require live network access. No Soroban RPC URL is needed.
| Job | Variable | Source | Value / Note |
|---|---|---|---|
chromatic |
NODE_OPTIONS |
inline env: |
--max_old_space_size=4096 — prevents OOM during Storybook build with large component library |
chromatic |
CHROMATIC_PROJECT_TOKEN |
secrets.CHROMATIC_PROJECT_TOKEN |
Required for Chromatic authentication. Workflow is skipped if unset (fork PRs). |
| Job | Variable | Source | Value / Note |
|---|---|---|---|
bundle-size |
CI |
inline env: |
true |
bundle-size |
NEXT_PUBLIC_STELLAR_NETWORK |
inline env: |
testnet — keeps the build consistent with lighthouse.yml |
bundle-size |
ANALYZE |
inline env: |
true — enables @next/bundle-analyzer output if configured |
| Job | Variable | Source | Value / Note |
|---|---|---|---|
| (none) | — | — | The audit script reads source files only; no env vars are needed at runtime. |
| Job | Variable | Source | Value / Note |
|---|---|---|---|
| (none) | — | — | Runs on the ubuntu-latest runner (not the custom runner). Uses npm ci for install. |
Gap: This workflow uses node: '20' (generic) rather than node-version-file: '.nvmrc'. This means mutation tests may run on a slightly different Node patch than CI. Consider aligning with .nvmrc.
| Job | Variable | Source | Value / Note |
|---|---|---|---|
| (none) | — | — | Uses ubuntu-latest. Only validates YAML syntax with actionlint. |
The following secrets are configured in the Vercel Production environment:
NEXT_PUBLIC_SUPABASE_URL- Supabase project URLNEXT_PUBLIC_SUPABASE_ANON_KEY- Supabase anonymous key (public-safe, scoped to client operations)
SUPABASE_SERVICE_ROLE_KEY- Supabase service role key (full admin access to Supabase)RESEND_API_KEY- Resend API key for email notificationsCRON_SECRET- Secret for securing cron job endpointsGITHUB_TOKEN- GitHub token for feedback-to-issue forwardingGITHUB_OWNER- GitHub repository ownerGITHUB_REPO- GitHub repository name
NEXT_PUBLIC_STELLAR_NETWORK- Network identifier (testnet/public)NEXT_PUBLIC_RPC_URL- Soroban RPC endpointNEXT_PUBLIC_CONTRACT_ID- Mainnet invoice factoring contract IDNEXT_PUBLIC_GOVERNANCE_CONTRACT_ID- Mainnet governance contract IDNEXT_PUBLIC_NFT_CONTRACT_ID- Mainnet NFT contract ID (if NFT display enabled)NEXT_PUBLIC_TESTNET_USDC_TOKEN_ID- USDC token contract IDNEXT_PUBLIC_TESTNET_EURC_TOKEN_ID- EURC token contract IDNEXT_PUBLIC_INDEXER_API_URL- Indexer API endpointNEXT_PUBLIC_INDEXER_WS_URL- Indexer websocket endpoint
- Current status: Audit required - verify who has admin/deploy access to the Vercel project
- Recommended access model:
- Admin access: Core maintainers only (2-3 individuals)
- Deploy access: Frontend leads + designated release managers
- Read-only access: All other contributors
- Production environment: All secrets should be protected with Vercel's environment variable protection
- Preview/Development environments: Must NOT have access to production secrets
- Preview deployments should use testnet-shaped values only
- No production Supabase service role keys in preview
- No production Resend API keys in preview
- No production contract IDs in preview
| Secret Type | Rotation Frequency | Rotation Procedure |
|---|---|---|
| Supabase Service Role Key | Quarterly (every 90 days) | Regenerate in Supabase dashboard → Update in Vercel Production → Redeploy |
| Supabase Anon Key | Annually or if compromised | Regenerate in Supabase dashboard → Update in Vercel Production → Redeploy |
| RESEND_API_KEY | Annually or if compromised | Regenerate in Resend dashboard → Update in Vercel Production → Redeploy |
| CRON_SECRET | Annually or if compromised | Generate new random string → Update in Vercel Production → Redeploy |
| GitHub Token | When GitHub account security changes | Revoke old token → Generate new token → Update in Vercel Production → Redeploy |
For each secret rotation:
-
Pre-rotation checklist
- Schedule rotation during low-traffic window
- Notify team of upcoming rotation
- Confirm current deployment is stable
- Have rollback plan ready (previous secret value)
-
Rotation steps
- Generate new secret value in the provider's dashboard
- Update the secret in Vercel Production environment
- Trigger a production deployment:
vercel --prod - Verify deployment succeeds and app functions correctly
- Run smoke tests against production
-
Post-rotation verification
- Confirm all API calls succeed with new secret
- Verify cron jobs execute successfully
- Check logs for authentication errors
- Document rotation date and new secret hash (not the value itself)
-
Cleanup
- Revoke old secret in provider's dashboard (after 24-48 hours of stable operation)
- Update this documentation with rotation date
Preview deployments (created from external contributor PRs) must never have access to:
- Production Supabase service role key
- Production Resend API key
- Production contract IDs (mainnet)
- Production RPC URLs (mainnet)
Run this verification after any Vercel configuration change:
# List all environment variables across environments
vercel env ls
# Verify Production-only secrets are not in Preview/Development
# Expected: SUPABASE_SERVICE_ROLE_KEY, RESEND_API_KEY should only appear under "Production"Preview deployments should use these defaults (set in Vercel Project Settings → Environment Variables):
| Variable | Preview Value | Production Value |
|---|---|---|
NEXT_PUBLIC_STELLAR_NETWORK |
testnet |
public |
NEXT_PUBLIC_RPC_URL |
Testnet RPC | Mainnet RPC |
NEXT_PUBLIC_CONTRACT_ID |
Testnet contract ID | Mainnet contract ID |
SUPABASE_SERVICE_ROLE_KEY |
Not set (or testnet-only) | Production key |
RESEND_API_KEY |
Not set (or test sandbox) | Production key |
If a secret is suspected to be compromised:
-
Immediate action
- Revoke the compromised secret in the provider's dashboard
- Rotate the secret immediately using the rotation procedure above
- If the secret was used for authentication, invalidate all active sessions
-
Incident response
- Follow the incident response procedure in docs/incident-response.md
- Determine the blast radius (what could an attacker access with this secret?)
- Audit logs for suspicious activity during the compromise window
-
Post-incident
- Review access controls and reduce permissions where possible
- Update this documentation with lessons learned
- Consider shortening the rotation cadence for similar secrets
Please verify and complete the following:
- Audit current Vercel team access and document who has admin/deploy permissions
- Confirm all production secrets are protected with Vercel environment variable protection
- Verify preview deployments do not have access to production secrets
- Schedule first quarterly rotation for Supabase service role key
- Document the rotation dates for each secret type in this section
-
Shared Node version: Workflows now consume
.nvmrcso local development and CI stay aligned on the same Node baseline. -
Dedicated accessibility workflow: Accessibility checks now live in a single workflow with one required
accessibilityjob, which avoids duplicate check names and redundant CI runs. -
E2E coverage on develop: The E2E suite now runs for both
mainanddeveloppushes and pull requests so regressions surface beforemainmerges. -
Feature flag build consistency: The
ci.ymlbuild job does not set feature flags. If a flag lacks a default insrc/lib/env.ts, it will silently build as disabled. All new flags must have a defined default. -
Mutation testing Node alignment:
mutation-testing.ymlusesnode: '20'(generic) instead ofnode-version-file: '.nvmrc'. Consider updating it to use the pinned version.
Please verify the following:
- Confirm the actual branch protection rules configured in GitHub repository settings match the documentation above
- Review and update custom runner configuration documentation if
namespace-profile-nurscasetup has changed - Add
bundle-sizeto branch protection required checks (once baseline is established) - Update
mutation-testing.ymlto usenode-version-file: '.nvmrc'for version consistency