Appearance
Multi-Repo Strategy
Why Nine Repositories (And Growing)
The VelaPay workspace has evolved from 5 repos to 11 across 8 milestones:
vela-labs/
├── vela-protocol/ # Anchor/Rust on-chain program (dual: protocol + transfer hook)
├── vela-sdk/ # TypeScript SDK + CLI + webhook event schemas
├── vela-dashboard/ # Merchant dashboard + backend API
├── vela-admin/ # Internal admin ops dashboard
├── vela-checkout/ # Hosted checkout at pay.velapay.com
├── vela-portal/ # Subscriber portal at portal.velapay.com
├── vela-widget/ # Embedded checkout for merchant pages
├── vela-webhook/ # Webhook event SDK (@vela/webhook)
├── vela-web/ # Landing page at velapay.com
├── vela-docs/ # Developer documentation at docs.velapay.com
└── vela-synthetic/ # E2E cron worker for staging validationEvolution Timeline
| Phase | Repo Count | What Was Added | Why |
|---|---|---|---|
| v1.0 | 5 | vela-protocol, vela-sdk, vela-dashboard, vela-web, vela-docs | Initial workspace — the billing primitive works |
| v1.1 | 6 | + vela-widget | Merchants needed embeddable checkout on their own pages |
| v1.2 | 7 | + vela-admin | Protocol team needed internal ops dashboard |
| v1.5 | 9 | + vela-checkout, vela-portal | Hosted billing surfaces for payment links and subscriber self-service |
| v1.8 | 11 | + vela-webhook, vela-synthetic | Typed webhook delivery and continuous staging validation |
Each new repo was added when a distinct runtime concern, auth model, or deploy lifecycle emerged that couldn't be cleanly contained in an existing repo.
Why Not Monorepo
A monorepo would add complexity without benefit for this project:
- Different deploy targets — Protocol deploys to Solana, dashboard to Railway, everything else to Cloudflare Workers. Monorepo CI would need 5+ deploy pipelines.
- Different toolchains — Rust for protocol, TypeScript for everything else. Shared tooling is limited to git.
- Different update cadences — SDK needs frequent small updates, protocol needs careful versioning. Monorepo would force coordinated releases.
- Different testing strategies —
cargo testwith LiteSVM for protocol,bun testfor TypeScript repos. Shared test infrastructure is minimal. - Different security models — Admin has wallet-gated SIWS auth, dashboard has email-primary auth, checkout/portal are public-facing with magic-link auth. Monorepo makes auth boundary management harder.
- Cleaner ownership — Each team (or agent session) works in their own repo without cross-contamination.
CI/CD Strategy Per Repo
| Repo | CI Pipeline | CD Target | Trigger |
|---|---|---|---|
vela-protocol | cargo test + anchor build | Solana devnet (manual deploy) | Push to master |
vela-sdk | bun test + tsc type check | npm publish (@vela/sdk) | Version tag |
vela-dashboard | bun test + Docker build | Railway (SSR) + Cloudflare (Worker, D1) | Push to master |
vela-admin | bun test + build | Cloudflare Workers | Push to master |
vela-checkout | bun test + build | Cloudflare Workers | Push to master |
vela-portal | bun test + build | Cloudflare Workers | Push to master |
vela-widget | bun test + build | CDN (Cloudflare Workers static) | Version tag |
vela-webhook | bun test + schema-diff check | npm publish (@vela/webhook) | Version tag |
vela-web | bun test + Astro build | Cloudflare Workers (static) | Push to master |
vela-docs | bun test + Astro build | Cloudflare Workers (static) | Push to master |
vela-synthetic | bun test + build | Cloudflare Workers (cron) | Push to master |
Special CI Patterns
- Schema-diff check (
vela-webhook): CI enforces additive-only event evolution. New event types are allowed; removing or changing existing events fails the build. - Staging E2E (
vela-synthetic): 15-minute cron against devnet staging. Pages on failure. - Playwright E2E (
vela-checkout,vela-portal): Full browser tests with Mailpit for email-dependent flows.
Cross-Repo Update Protocol
Protocol → SDK → Consumers
When vela-protocol updates, a specific chain of updates is required:
1. vela-protocol: Make changes, run cargo test, anchor build
↓ IDL output
2. vela-sdk: Copy new IDL, update instruction builders, run bun test
↓ published to npm
3. vela-dashboard: Update @vela/sdk dependency, adapt to API changes
vela-checkout: Update @vela/sdk dependency, adapt to API changes
vela-portal: Update @vela/sdk dependency, adapt to API changes
vela-widget: Update @vela/sdk dependency, adapt to API changes
vela-webhook: Update @vela/sdk dependency, adapt to event schemasVersion Compatibility Rules
- Protocol ↔ SDK: Must match exactly. SDK encodes program IDL, instruction discriminators, and account layouts.
- SDK → Consumers: Semantic versioning. Breaking SDK changes require major version bump.
- Consumers → Each Other: No direct dependencies. Communication via shared D1 database and on-chain state.
Coordinated Change Protocol
- Update protocol first (source of truth).
- Update SDK second (encoding layer).
- Update consumers last (product surfaces).
- Use conventional commits for tracking (
feat(protocol):,feat(sdk):,feat(dashboard):). - Each step is a separate PR and deploy.
Why vela-checkout and vela-portal Are Separate
From the Dashboard
vela-checkout and vela-portal were extracted from vela-dashboard in v1.5 because they have fundamentally different runtime concerns:
| Dimension | vela-dashboard | vela-checkout | vela-portal |
|---|---|---|---|
| Auth model | Email-primary + org membership | Public (Turnstile-gated) | Magic-link + SIWS |
| Audience | Merchants (authenticated) | Subscribers (anonymous) | Subscribers (authenticated) |
| Runtime | Railway SSR + CF Worker API | CF Worker only | CF Worker only |
| Deploy cadence | Changes with merchant features | Changes with checkout UX | Changes with portal features |
| Security boundary | Internal — merchant data | Public — payment flow | Public — subscription management |
| Domain | app.velapay.com | pay.velapay.com | portal.velapay.com |
Why Not Merge Them
Merging checkout or portal into the dashboard would:
- Expose merchant auth boundaries to public traffic.
- Force a single deploy cycle for merchant features and subscriber UX.
- Create a larger attack surface (checkout doesn't need merchant auth, portal doesn't need merchant data).
- Make independent scaling harder (checkout has bursty traffic during payment campaigns).
How They Communicate
Checkout and portal share patterns with the dashboard:
- Checkout session creation: Both
vela-checkoutandvela-portal(for plan switching) create checkout sessions via a shared pattern that writes to D1. - Public billing apps proxy to dashboard internal routes: Some API endpoints are shared, with the dashboard Worker handling the backend logic.
- D1 database: Shared database for checkout sessions, subscription state, and billing records.
Trade-offs Analysis
What Multi-Repo Gives Us
| Benefit | Impact |
|---|---|
| Independent deploys | Protocol can be redeployed without touching the dashboard. Checkout can be updated without touching the widget. |
| Independent versioning | SDK can publish a patch without coordinating with protocol. Widget can version independently from docs. |
| Clearer ownership | Each repo has a clear owner, clear test suite, clear deploy pipeline. |
| Smaller clone/fetch size | Only clone what you need. Protocol developers don't need checkout code. |
| Optimal toolchain per repo | Rust for protocol, TypeScript+Hono for Workers, Astro for docs. No compromise. |
| Auth boundary isolation | Each repo enforces its own auth model without cross-contamination. |
What Multi-Repo Costs
| Cost | Mitigation |
|---|---|
| No atomic commits across repos | Conventional commits + update protocol. Accept that cross-repo changes require coordination. |
| Shared config managed separately | @biomejs/biome config is similar but not shared. Acceptable divergence. |
| Cross-repo refactoring is harder | Protocol versioning (v1.7 reserved space) makes additive changes the default. Breaking changes are rare and planned. |
| More CI pipelines to maintain | Each pipeline is simple (build + test + deploy). Complexity per pipeline is low. |
| Dependency drift across repos | Workspace-level bun.lock anchors versions. SDK version pinning prevents drift. |
When Monorepo Would Be Better
If the team grows to 5+ engineers working on the same product surfaces, monorepo benefits (atomic commits, shared CI, unified versioning) might outweigh the deploy isolation benefits. For the current team size and architecture, multi-repo is the right choice.
GitHub Organization Structure
All repos live under the GitHub organization:
github.com/vela-labs/
├── vela-protocol # Anchor/Rust on-chain program
├── vela-sdk # TypeScript SDK + CLI
├── vela-dashboard # Merchant dashboard
├── vela-admin # Internal admin dashboard
├── vela-checkout # Hosted checkout
├── vela-portal # Subscriber portal
├── vela-widget # Embedded checkout
├── vela-webhook # Webhook event SDK
├── vela-web # Landing page
├── vela-docs # Developer documentation
├── vela-synthetic # E2E cron worker
└── (internal-wiki) # Internal wiki (separate repo or workspace directory)Branch Strategy
master: Default branch for all repos. Protected by CI.- Feature branches: Used during development, merged via PR.
- No release branches: Continuous deployment from master.
Workspace-Level Files
The workspace root (vela-labs/) contains:
.planning/: Authoritative project state (requirements, roadmap, state).internal-wiki/: This wiki.- Root strategy docs (
01-vela-labs-brand.mdthrough10-future-vision-layer3-and-beyond.md): Founding context. CLAUDE.md/AGENTS.md: AI agent instructions shared across repos.docker-compose.staging.yml: Staging environment for E2E tests.
Shared Dependencies
All TypeScript repos share these dependencies (managed separately per repo):
| Package | Version | Purpose |
|---|---|---|
typescript | 5.8+ | Type checking |
@biomejs/biome | latest | Linting + formatting |
wrangler | 4.78.x | Cloudflare CLI (Workers repos) |
@vela/sdk | latest | Protocol client (all product repos) |
No shared package.json — each repo manages its own dependencies independently.
.claude Directory
Each repo has a .claude/ directory with:
- Project context (CLAUDE.md with repo-specific instructions).
- Agent instructions for automated development.
- Repository-specific conventions and constraints.
This enables AI agents to work effectively in any repo without cross-repo context.