Development Process
Branch strategy, PR workflow, plugin team independence, and contribution guidelines for the NaaP platform.
Overview#
NaaP is a monorepo that powers the Livepeer Network-as-a-Platform. It is maintained by core contributors who own the shell, shared packages, and infrastructure, and by plugin teams who independently build and ship features within their plugin directories.
This guide covers everything you need to know to contribute effectively — whether you are a new plugin team, a core contributor, or a first-time open source contributor.
Repository Layout#
| 1 | livepeer/naap ← upstream (public) |
| 2 | ├── apps/web-next/ ← Next.js shell (core team) |
| 3 | ├── packages/ ← shared SDK, types, utils (core team) |
| 4 | ├── services/ ← core backend services (core team) |
| 5 | ├── plugins/ |
| 6 | │ ├── gateway-manager/ ← plugin team A owns this |
| 7 | │ ├── community/ ← plugin team B owns this |
| 8 | │ ├── daydream-video/ ← plugin team C owns this |
| 9 | │ └── ... ← each team owns their directory |
| 10 | ├── .github/workflows/ ← CI/CD (core team) |
| 11 | ├── bin/ ← platform scripts (core team) |
| 12 | └── docker/ ← infrastructure (core team) |
Key principle: Plugin teams own their plugins/<name>/ directory. Core contributors own everything else. Ownership is enforced via CODEOWNERS.
Branch Strategy#
NaaP uses a feature-branch-off-main (trunk-based) model. All PRs target main directly. Vercel PR previews serve as staging for every PR.
| 1 | feature branches main (production) |
| 2 | ───────────────── ───────────────── |
| 3 | |
| 4 | feat/video/webrtc ─── PR ──────────►│ │ |
| 5 | (preview URL) │ auto-deploy │ |
| 6 | fix/social/auth ───── PR ──────────►│ to production │ |
| 7 | (preview URL) │ on merge │ |
| 8 | feat/infra/cache ──── PR ──────────►│ │ |
| 9 | (preview URL) │ │ |
| Branch | Purpose | Deploys To | Who Merges |
|---|---|---|---|
main | Production. Single source of truth. | Production (auto) | Plugin teams + core (via merge queue) |
feat/<team>/<desc> | New features | PR preview (auto) | Author opens PR |
fix/<team>/<desc> | Bug fixes | PR preview (auto) | Author opens PR |
chore/<desc> | Docs, CI, tooling | PR preview (auto) | Author opens PR |
Branch naming conventions#
PR Process — Step by Step#
1. Create your branch#
2. Make your changes#
- Start developing:
./bin/start.sh(or./bin/start.sh my-plugin) - Make focused, incremental changes
- Write tests for new functionality
- Follow Conventional Commits for all commit messages
3. Open a PR against main#
4. Automated checks run#
Once your PR is open, the following happens automatically:
| Step | What happens | Who is responsible |
|---|---|---|
| Labeler | Auto-labels PR based on changed file paths | Bot |
| CODEOWNERS | Assigns reviewers — your team for plugin code, core team for shared code | Bot |
| CI | Runs path-filtered tests — only affected packages are tested | Bot |
| Copilot | AI code review with project-specific context | Bot |
| CodeRabbit | AI code review with request-changes workflow | Bot |
| Commit lint | Validates commit messages follow Conventional Commits | Bot |
| PR size | Warns if PR exceeds 400 changed lines | Bot |
| Conflict check | Alerts if PR conflicts with base branch | Bot |
| Vercel preview | Deploys a preview URL for testing | Bot |
5. Review and iterate#
- Plugin-only PRs: your team reviews and approves
- Cross-cutting PRs: core team is auto-assigned and must also approve
- All review conversations must be resolved before merge
6. Merge#
Once approved and CI passes, the PR enters the merge queue and merges automatically. Changes are auto-deployed to production with health checks.
7. Release tagging (on-demand)#
A core maintainer can trigger the "Tag Release" workflow to create a versioned release:
- Run the "Tag Release" workflow from GitHub Actions
- Specify a version (e.g.,
v1.2.0) - A Git tag and GitHub Release are created with auto-generated notes
For Plugin Teams#
You own your directory#
Your team has full autonomy over plugins/<your-plugin>/. This includes:
- Frontend (
plugins/<name>/frontend/) — React components, styles, state - Backend (
plugins/<name>/backend/) — Express API, business logic - Config (
plugins/<name>/plugin.json) — manifest, routes, permissions
You decide code style, review standards, and internal conventions within your directory.
What you CAN do (freely)#
- Ship features, fixes, and refactors within your plugin directory
- Review and approve your own team's PRs (no core team needed)
- Add dependencies to your plugin's
package.json - Create your own database tables in your plugin's schema
- Choose your own state management, UI patterns, and testing strategy
- Deploy to production by merging to
main
Plugin team boundaries#
- Do NOT import from other plugins. Use the event bus (
@naap/plugin-sdk) for cross-plugin communication - Do NOT modify shared packages (
packages/*) without core team review - Do NOT modify the shell (
apps/web-next/) without core team review - Do NOT hardcode your plugin in the shell. Everything is driven by
plugin.jsonmanifests - Do NOT modify CI workflows (
.github/workflows/) without core team review - Do NOT commit secrets (
.envfiles, API keys) — these are gitignored
Setting up your plugin development#
Plugin directory structure#
| 1 | plugins/your-plugin/ |
| 2 | ├── plugin.json ← manifest (routes, permissions, config) |
| 3 | ├── frontend/ |
| 4 | │ ├── package.json |
| 5 | │ ├── src/ |
| 6 | │ │ ├── App.tsx ← main component (loaded by shell) |
| 7 | │ │ ├── components/ ← your UI components |
| 8 | │ │ └── hooks/ ← your custom hooks |
| 9 | │ └── vite.config.ts ← build config (UMD bundle) |
| 10 | ├── backend/ |
| 11 | │ ├── package.json |
| 12 | │ ├── src/ |
| 13 | │ │ ├── server.ts ← Express server |
| 14 | │ │ ├── routes/ ← API routes |
| 15 | │ │ └── services/ ← business logic |
| 16 | │ └── .env ← local config (gitignored) |
| 17 | └── README.md |
Your plugin.json is your contract#
The plugin.json manifest tells the shell everything it needs to know:
| 1 | { |
| 2 | "name": "myPlugin", |
| 3 | "displayName": "My Plugin", |
| 4 | "version": "1.0.0", |
| 5 | "frontend": { |
| 6 | "entry": "dist/production/my-plugin.js", |
| 7 | "devPort": 5180, |
| 8 | "routes": ["/my-route", "/my-route/*"], |
| 9 | "navigation": { |
| 10 | "label": "My Plugin", |
| 11 | "icon": "Star", |
| 12 | "order": 10 |
| 13 | } |
| 14 | }, |
| 15 | "backend": { |
| 16 | "entry": "dist/server.js", |
| 17 | "devPort": 4020, |
| 18 | "healthCheck": "/healthz", |
| 19 | "apiPrefix": "/api/v1/my-plugin" |
| 20 | }, |
| 21 | "permissions": { |
| 22 | "shell": ["navigation", "theme", "notifications"] |
| 23 | } |
| 24 | } |
Daily workflow for plugin teams#
For Core Contributors#
Core contributors maintain the platform that all plugin teams build on. Your work directly affects every team's productivity.
What you own#
- Shell (
apps/web-next/) — the Next.js host that loads plugins - Shared packages (
packages/*) — SDK, types, utils, theme, UI components - Core services (
services/*) — base-svc, plugin-server, infrastructure - Infrastructure (
bin/,docker/,.github/) — scripts, CI/CD, Docker
What you should do#
- Review cross-cutting PRs promptly. Plugin teams are blocked until you review changes that touch shared code. Aim for < 24h review SLA
- Keep the SDK stable. Breaking changes require an RFC, migration guide, and version bump
- Maintain CI health. Fix flaky tests immediately — they erode trust
- Tag releases as needed. Use the "Tag Release" workflow to create versioned releases
- Document changes. SDK changes need updated docs before merge
Core team boundaries#
- Do NOT merge plugin-only PRs. Let plugin teams self-govern their code
- Do NOT make breaking SDK changes without an RFC. Plugin teams depend on stable APIs
- Do NOT bypass CI. Even urgent fixes must pass automated checks
- Do NOT block plugin teams unnecessarily. If a cross-cutting change is low-risk, review and approve quickly
- Do NOT force-push to
main. It is a protected branch
Handling cross-cutting changes#
When a plugin team needs a change in shared code:
- Plugin team opens an issue describing the need
- Core team evaluates and either:
- Makes the change themselves, or
- Guides the plugin team to submit a PR with core team as reviewer
- The PR requires both plugin team approval AND core team approval
- CI runs the SDK compatibility matrix to ensure no regressions
Deployment-Safe Development#
NaaP deploys to Vercel (production). Plugins are built as UMD bundles and served from the same origin. Understanding the deployment model prevents the most common breakages.
How It Works on Vercel#
| 1 | Local Dev Vercel Production |
| 2 | ───────── ────────────────── |
| 3 | Shell: localhost:3000 naap.vercel.app (Next.js) |
| 4 | Plugin UI: loaded via CDN bundle loaded via /cdn/plugins/... |
| 5 | Plugin API: localhost:4006 /api/v1/community (same-origin proxy) |
| 6 | Database: localhost:5432 Neon PostgreSQL (via DATABASE_URL) |
On Vercel, there are no separate backend processes — all API calls go through the Next.js API proxy at the same origin. Plugin frontends must use deployment-aware URL resolution.
Rules for Plugin Developers#
Always do:
- Use
getPluginBackendUrl()from@naap/plugin-sdkfor all API URLs - Use
usePluginApi()hook for API calls in React components - Use
createPluginConfig()from@naap/plugin-build/vitefor your Vite config - Keep your
tailwind.config.js(per-plugin theme/content paths) - Test your plugin loads correctly at
/plugins/<your-plugin>after building
Never do:
- Hardcode
localhost:PORTin frontend code (breaks on Vercel) - Hardcode
window.location.hostname + ':' + port(appends dev port to production domain) - Add a
postcss.config.jsto your plugin (breaks Vercel builds — PostCSS is in the shared Vite config) - Use
*/inside JSDoc comments (e.g.,plugins/*/plugin.json— the*/closes the comment and breaks esbuild) - Use
((count++))in bash scripts withset -e(when count=0, returns exit code 1)
API URL Quick Reference#
| 1 | // In React components (recommended): |
| 2 | import { usePluginApi } from '@naap/plugin-sdk'; |
| 3 | const api = usePluginApi(); |
| 4 | const data = await api.get('/api/v1/my-plugin/items'); |
| 5 | |
| 6 | // In non-React code: |
| 7 | import { getPluginBackendUrl } from '@naap/plugin-sdk'; |
| 8 | const BASE = getPluginBackendUrl('my-plugin', { apiPath: '/api/v1/my-plugin' }); |
| 9 | // Local → http://localhost:4020/api/v1/my-plugin |
| 10 | // Vercel → /api/v1/my-plugin |
| 11 | |
| 12 | // WRONG — will break on Vercel: |
| 13 | const BASE = 'http://localhost:4020/api/v1/my-plugin'; |
Commit Conventions#
Use Conventional Commits. This is enforced by CI.
<type>(<scope>): <short description>Types#
| Type | When to use |
|---|---|
feat | New feature or capability |
fix | Bug fix |
refactor | Code restructuring, no behavior change |
docs | Documentation only |
test | Adding or updating tests |
chore | Tooling, CI, dependencies |
perf | Performance improvement |
Scopes#
| Scope | Example |
|---|---|
shell | feat(shell): add plugin error boundary |
sdk | feat(sdk): add usePluginConfig hook |
plugin/<name> | fix(plugin/community): handle empty post list |
base-svc | refactor(base-svc): extract auth routes |
infra | chore(infra): upgrade postgres to 16 |
ci | chore(ci): add e2e test matrix |
docs | docs(sdk): update hook reference |
Hotfix Process#
Since all PRs target main, the hotfix process is the same as normal development:
Hotfixes go through the same PR and CI process as any other change. The merge queue ensures main stays green.
Testing Requirements#
| Change type | Required tests |
|---|---|
| SDK / shared packages | Unit tests. Coverage must not decrease |
| Plugin backend | Health check + basic endpoint tests |
| Plugin frontend | Component tests for critical flows |
| Shell changes | Manual smoke test with 2+ plugins loaded |
| Breaking changes | Migration notes in PR description |
Run tests before pushing:
Environment and Tools#
| Tool | Version | Purpose |
|---|---|---|
| Node.js | 20+ | Runtime |
| npm | 10+ | Package manager (workspaces) |
| Docker | Latest | PostgreSQL database |
| TypeScript | ~5.8 | Type safety |
| React | 19 | UI framework |
| Next.js | 15.5 | Shell framework |
| Vite | 6 | Plugin builds |
| Nx | 22 | Monorepo task runner |
Quick Reference#
Everyday commands (plugin teams)#
First-time & full commands#
Core contributor commands#
Login credentials (dev environment)#
| Password | Role | |
|---|---|---|
admin@livepeer.org | livepeer | System Admin |
viewer@livepeer.org | livepeer | Read-only Viewer |
gateway@livepeer.org | livepeer | Gateway Manager Admin |
community@livepeer.org | livepeer | Community Admin |
developer@livepeer.org | livepeer | Developer API Admin |
All 12 test users share the same password: livepeer
Summary#
| Plugin Teams | Core Contributors | |
|---|---|---|
| Own | plugins/<name>/ | Shell, packages, services, infra |
| PR target | main | main |
| Reviewers | Your own team (auto-assigned) | Core team (auto-assigned) |
| Can merge | Plugin-only PRs | Cross-cutting PRs, tag releases |
| Deployment | Auto-deploy to production on merge | Auto-deploy + rollback |
| Independence | Full autonomy within your directory | Steward the platform for all teams |