ENGINEERING SPECS
Development Framework Specification
1. Goal
This document defines the implementation framework and engineering conventions for OrbitMesh.
Selected stack:
Control Plane: Go + Kratos
Install Service: Cloudflare Workers
Console: Next.js static export on Cloudflare Pages
Edge Runtime: Go standalone binary
Database: PostgreSQL default, MySQL supported
Cache: Redis
Data Plane Runtime: sing-box
2. Architecture Boundary
OrbitMesh is implemented as a cloud-native control-plane system.
The boundary is:
- Control Plane owns product state, APIs, reconciliation, compilation, policy, health, billing, and audit.
- Install Service owns the public
install.orbitmesh.deventrypoint and serves installer scripts. - Console owns presentation, login pages, forms, charts, and user workflows.
- Edge Runtime owns node-side identity, sync loops, runtime application, rollback, probes, and local supervision.
- Data Plane Runtime owns packet forwarding and protocol-specific behavior.
Do not move Control Plane responsibilities into Next.js route handlers.
Do not make the Install Service a Control Plane API. It is a thin Cloudflare Workers edge entrypoint for install scripts.
3. Control Plane Framework
Use Go + Kratos for the Control Plane.
Required conventions:
- API definitions are protobuf-first.
- HTTP APIs are exposed through Kratos HTTP transport.
- gRPC APIs can be enabled for internal or future streaming use cases.
- Business logic lives in
internal/biz. - Transport handlers live in
internal/service. - Database and Redis implementations live in
internal/data. - HTTP/gRPC bootstrap lives in
internal/server. - Runtime-specific compilation lives in
internal/compiler. - Middleware is registered in Kratos server setup, not inside business use cases.
The MVP is a modular monolith. Do not split into microservices before the core lifecycle is stable.
4. Kratos Project Layout
services/control-plane/
api/
orbitmesh/
v1/
auth.proto
tenant.proto
node.proto
capability.proto
runtime.proto
endpoint.proto
deployment.proto
desired_state.proto
traffic_policy.proto
subscription.proto
health.proto
edge_runtime.proto
cmd/
orbitmesh-api/
main.go
internal/
service/
biz/
auth/
tenant/
node/
capability/
runtime/
endpoint/
deployment/
desiredstate/
trafficpolicy/
subscription/
health/
billing/
data/
postgres/
redis/
server/
http.go
grpc.go
conf/
middleware/
compiler/
event/
ent/
schema/
configs/
Dockerfile
Layer rules:
serviceconverts transport DTOs to use-case inputs and maps errors.bizowns resource lifecycle, validation, state transitions, and authorization decisions.dataowns SQL, Redis, transactions, and repository implementations.compilermapsDesiredStatetoRuntimeConfiguration; it must not call HTTP handlers.eventrecords domain events and can later back async workers.
5. API Design
API groups:
AuthServiceTenantServiceNodeServiceCapabilityServiceRuntimeServiceEndpointServiceDeploymentServiceDesiredStateServiceTrafficPolicyServiceSubscriptionServiceHealthServiceEdgeRuntimeService
HTTP prefix:
/api/v1
JSON conventions:
- Use snake_case field names to match protobuf source names and existing API examples.
- Use UTC RFC3339 timestamps.
- Use string IDs with resource prefixes only when the generator or model layer supports them consistently.
- Use
request_idin every error response. - Use
Idempotency-Keyfor mutating Tenant Console API requests.
Kratos HTTP handlers should use protobuf annotations for routing and generate OpenAPI from the same contract where possible.
Detailed API and backend behavior contracts:
specs/engineering-scaffold.md: repository bootstrap, tools, Make targets, Ent schema, and generated code rules.specs/proto-contracts.md: protobuf services, messages, errors, and compatibility rules.specs/state-machines.md: resource states and legal transitions.specs/use-case-transactions.md: transactional behavior for core use cases.
6. Error Model
Use Kratos errors with stable business reasons.
Error response shape:
{
"request_id": "req_xxx",
"error": {
"code": "DEPLOYMENT_TOKEN_EXPIRED",
"message": "deployment token expired"
}
}
Rules:
codeis stable and uppercase.messageis safe to show to users.- Internal errors are logged with
request_idand returned asINTERNAL. - Edge Runtime authentication failures return
401. - Tenant authorization failures return
403. - Missing resources return
404. - Invalid resource state returns
409. - Validation failures return
400.
7. Persistence
Use an Ent-managed SQL database as source of truth. PostgreSQL with pgx is the default. MySQL 8 is supported through ORBITMESH_DATABASE_DRIVER=mysql.
Recommended implementation:
database/sqlwithpgxormysqldriver mapping.- MySQL DSNs include
parseTime=true,charset=utf8mb4, and UTC time handling. - Ent schema in
services/control-plane/ent/schema. - Ent schema creation during Control Plane startup through
Schema.Create. - Repository interfaces defined in
internal/biz. - Repository implementations in
internal/data. - Redis for nonce, rate limit, short-lived cache, and distributed locks.
Rules:
- Database writes that change Node registration must run in a single transaction.
- Deployment token plaintext is returned once and only
token_hashis stored. - Console must not connect directly to the Control Plane database.
- Prisma is not used as the source of truth for OrbitMesh backend domain logic.
8. Control Plane Middleware
Required middleware:
- request ID.
- structured logging.
- panic recovery.
- authentication.
- tenant context.
- permission check.
- rate limit.
- idempotency.
- Edge Runtime request signing verification.
- unified error mapping.
Headers:
X-Request-Id
X-OrbitMesh-Tenant
Idempotency-Key
X-OrbitMesh-Node-Id
X-OrbitMesh-Timestamp
X-OrbitMesh-Nonce
X-OrbitMesh-Signature
9. Console Framework
Use Next.js App Router with static export and deploy to Cloudflare Pages.
Recommended stack:
Next.js App Router
TypeScript
lucide-react
Cloudflare Pages
The Tenant Console should follow this deployment shape:
apps/consoleowns tenant-console workflows.apps/platformowns platform-operator workflows.apps/webis reserved for the marketing website.- component-driven UI.
- Cloudflare Pages deployment.
- typed environment variables.
- reusable API client layer.
OrbitMesh must differ from that reference in one important way:
- Core backend APIs stay in Go Kratos.
- Next.js route handlers are allowed only for frontend-owned utilities such as auth callbacks or image/OG generation.
- Next.js route handlers must not implement Node lifecycle, DesiredState, Edge Runtime sync, billing entitlement, or health aggregation.
- Platform operator workflows such as plan catalog management, tenant plan assignment, managed resource pools, global runtime catalog administration, and system certificate/DNS operations must be implemented in
apps/platform, notapps/console.
Suggested layout:
apps/console/
app/
(console)/
nodes/
nodes/enroll/
traffic/subscriptions/
traffic/policies/
traffic/usage/
network/endpoints/
network/domains/
runtime-catalog/
settings/
login/
components/
console/
ui/
lib/
api-client.ts
auth.ts
env.ts
hooks/
public/
Platform Console layout:
apps/platform/
app/
plans/
tenants/
managed-nodes/
runtime-catalog/
subscription-targets/
domains/
certificates/
audit/
components/
lib/
hooks/
public/
10. Tenant Console API Client
The Tenant Console must call the Go Control Plane API.
Rules:
- API calls go through
apps/console/app/lib/api-client.ts. - Do not scatter raw
fetchcalls across pages. - Server components may fetch read-only data.
- Client components may call mutation helpers for user actions.
- Auth/session state is mapped to Control Plane credentials or session tokens.
- Tenant context is sent explicitly through
X-OrbitMesh-Tenant. - API errors are displayed from the stable error
codeand user-safemessage. - Mutation feedback must use semantic presentation consistently: success is green, validation/API errors are red, warnings are amber, and informational notices use the neutral brand tone.
- Success and error messages must not share a generic error-styled notice component.
- Console API requests must use the typed OpenAPI client from
apps/console/app/lib/api-client.ts. - New Console API response and request types should come from the generated OpenAPI types instead of hand-written local interfaces.
- Hand-written Console types are allowed only for view models, component state, and derived UI data that do not directly mirror API payloads.
10.1 Tenant Console OpenAPI Client Generation
The Tenant Console uses a generated TypeScript SDK from the Control Plane OpenAPI document. Application code calls generated SDK functions and uses apps/console/app/lib/api-client.ts only for runtime client configuration and error normalization.
Source contract:
services/control-plane/api/orbitmesh/v1/*.proto
Generated Control Plane artifacts:
services/control-plane/api/openapi.yaml
services/control-plane/api/orbitmesh/v1/*.pb.go
services/control-plane/api/orbitmesh/v1/*_grpc.pb.go
services/control-plane/api/orbitmesh/v1/*_http.pb.go
Generated Console artifact:
apps/console/app/generated/client/
Generated Platform Console artifact:
apps/platform/app/generated/client/
Generation flow:
make proto
make api-client
The app-specific generation commands are:
pnpm --filter @orbitmesh/console generate:openapi
pnpm --filter @orbitmesh/platform generate:openapi
Field naming rules:
- OpenAPI generation must use
naming=proto. - OpenAPI generation must use
enum_type=stringso proto enum fields are exposed as string enum values in TypeScript SDK types. - API schema fields, query parameters, and path parameters are
snake_case. - Console code must treat generated OpenAPI types as
snake_case. - Do not introduce camelCase aliases in generated client types.
- If a component needs camelCase for local derived state, map it explicitly at the feature boundary.
Typed API usage:
import { subscriptionServiceRotateSubscription } from "./generated/client";
import { apiClient, apiData } from "./lib/api-client";
const client = apiClient({ apiURL, token, tenantId });
const response = await apiData(
subscriptionServiceRotateSubscription({
client,
path: { id },
body: { id },
}),
{ operation: "SubscriptionService_RotateSubscription", token },
);
Client rules:
- Use generated SDK functions from
apps/console/app/generated/clientfor endpoints present in OpenAPI. - Do not call OpenAPI paths directly from feature code.
- Put path and query parameters under
pathandquery. - Put request payloads under
body; non-trivial payloads should use generated request types fromapps/console/app/generated/client/types.gen.ts. - Console-facing Control Plane APIs must be present in OpenAPI.
/healthzand/readyzremain probe-only endpoints and must not be called from Console feature code. - Do not add new hand-written API mirror types.
- API-backed console domain types should be derived with
NormalizedOpenAPI<...>inapps/console/app/types/console.ts; keep form state, row models, and view models as local UI types.
Regeneration is required when proto contracts change. A generated artifact change is expected for:
services/control-plane/api/openapi.yamldocs/api/openapi.yamlapps/console/app/generated/client/
CI should fail if OpenAPI or Console generated types are stale after proto changes.
Environment variables:
NEXT_PUBLIC_ORBITMESH_API_URL
NEXTAUTH_URL
NEXTAUTH_SECRET
11. Domain Plan
OrbitMesh uses orbitmesh.dev as the only public domain family for this project during MVP and early production.
Reserved domain boundaries:
orbitmesh.cloud reserved for the separate virtual network SaaS product
orbitmesh.host reserved for the separate virtual network SaaS product
orbitmesh.cc reserved for the separate virtual network SaaS product or defensive redirect
orbitmesh.app reserved for applications built with OrbitMesh, not this platform surface
Required orbitmesh.dev allocation:
orbitmesh.dev marketing site and product entry
docs.orbitmesh.dev documentation
console.orbitmesh.dev Tenant Console
platform.orbitmesh.dev Platform Console
api.orbitmesh.dev Control Plane API
auth.orbitmesh.dev authentication callbacks and identity surface
install.orbitmesh.dev public installer entrypoint
downloads.orbitmesh.dev Edge Runtime artifact CDN prefix
status.orbitmesh.dev public status page
Network-facing names should stay under delegated edge.orbitmesh.dev subzones and must be modeled by resource instance. A tenant can own multiple Traffic Entry endpoints, Gateway endpoints, and Tunnel endpoints.
<node-id>.node.edge.orbitmesh.dev
<endpoint-id>.traffic.edge.orbitmesh.dev
<tenant-slug>--<endpoint-slug>.traffic.edge.orbitmesh.dev
<endpoint-id>.gateway.edge.orbitmesh.dev
<endpoint-id>.tunnel.edge.orbitmesh.dev
Traffic Entry names are externally reachable client access endpoints. User-owned domains should CNAME to the system endpoint name.
TLS certificate ownership is split into two scopes:
system certificate platform-managed certificate for system endpoint names
tenant certificate tenant-managed or tenant-requested certificate for custom domains
System-level ACME automation owns default endpoint names and renews certificates without tenant action.
Tenant-level ACME automation validates custom domain ownership before issuance. Tenant custom domains should CNAME to the system endpoint name. DNS-01 may be added later for wildcard or provider-integrated issuance.
Control Plane stores certificate metadata, status, expiration, issuer, DNS names, and secret references. It must not store private keys as plaintext database fields.
Implementation boundaries:
- Domain allocation, ownership validation, Certificate metadata, ACME order state, and Endpoint readiness live in Control Plane.
- DNS changes must go through a provider abstraction. The first production provider is Cloudflare for delegated
edge.orbitmesh.devzones. - Certificate private keys must live behind
secret_ref, local Node materialization, Kubernetes Secret, or an external secret manager. Do not add plaintext key columns. - Runtime adapters consume domain, certificate_ref, and resolved local certificate paths. They must not implement their own tenant certificate lifecycle.
- Subscription distribution reads ready Endpoint records only. It must not create ACME orders or publish DNS records.
Rules:
- Do not use
orbitmesh.cloud,orbitmesh.host,orbitmesh.cc, ororbitmesh.appfor this project's control plane, tenant console, platform console, installer, or data plane. - Keep one canonical SEO and product entry domain:
orbitmesh.dev. - All public endpoints must use HTTPS.
ORBITMESH_PUBLIC_URLshould usehttps://api.orbitmesh.devin production.NEXT_PUBLIC_ORBITMESH_API_URLshould usehttps://api.orbitmesh.devin production.ORBITMESH_INSTALL_ENDPOINT_URLshould usehttps://install.orbitmesh.devin production.ORBITMESH_EDGE_RUNTIME_DOWNLOAD_PREFIXshould usehttps://downloads.orbitmesh.dev/edge-runtime/latestin production.- Endpoint DNS names must not be allocated only by tenant. They must include endpoint or node identity.
- Local development may continue to use
localhost,127.0.0.1, and Control Plane/downloadsfallback paths.
12. Edge Runtime Framework
Edge Runtime is a Go standalone program.
Repository package path:
edge-runtime/
Command names:
orbitmesh
orbitmeshd
It owns:
- registration.
- heartbeat.
- desired state sync.
- runtime config apply.
- sing-box adapter.
- health probe.
- rollback.
- local state persistence.
It may use generated API client code. It must not import Kratos server framework.
13. Edge Runtime Local Conventions
Local paths:
/usr/local/bin/orbitmesh
/usr/local/bin/orbitmeshd
/etc/orbitmesh/edge-runtime.yaml
/etc/orbitmesh/node.json
/etc/orbitmesh/secrets.json
/var/lib/orbitmesh/edge-runtime/desired-state.json
/var/lib/orbitmesh/edge-runtime/runtime-configuration.json
/var/lib/orbitmesh/edge-runtime/runtime-configuration.last-good.json
/var/log/orbitmesh/edge-runtime.log
/etc/systemd/system/orbitmeshd.service
Runtime rules:
- Control Plane is pulled, not pushed over SSH.
- Last known good config is retained locally.
- Runtime config writes are atomic.
- runtime adapters run their configured validation command before reload.
orbitmeshdinstalls runtime dependencies from Control Plane runtime install profiles after pulling desired state; the public installer only installs OrbitMesh binaries and starts the daemon.- failed apply results are reported with a safe error.
- logs must redact deployment tokens, device private keys, and runtime passwords.
14. Development Commands
Recommended local flow:
docker compose up -d postgres redis
go run ./services/control-plane/cmd/orbitmesh-api
go run ./edge-runtime/cmd/orbitmesh --help
cd apps/console && pnpm run dev
CI gates:
go test ./...
go vet ./...
openapi validation
Ent schema generation check
cd apps/console && pnpm run lint
cd apps/console && pnpm run type-check
Recommended top-level Make targets:
make proto
make openapi
make api-client
make migrate
make test
make dev-control-plane
make dev-edge-runtime
make dev-console
make docker-up
make docker-down
Target ownership:
make proto: generate Kratos HTTP/gRPC code from proto files.make openapi: generate or validate OpenAPI from proto contracts.make api-client: generate Tenant Console OpenAPI TypeScript types and Edge Runtime clients.make migrate: apply Ent schema migration to the configured local SQL database.make test: run Control Plane and Edge Runtime tests.make dev-control-plane: run local Kratos API.make dev-edge-runtime: run local Edge Runtime with fake adapter or--help.make dev-console: runapps/consolelocally.make docker-up: start PostgreSQL, Redis, and optional Control Plane.make docker-down: stop local dependencies.
15. Testing Rules
Control Plane tests must cover:
- DeploymentToken one-time use.
- Node registration transaction.
- Edge Runtime request signing and replay nonce.
- DesiredState versioning.
- TrafficPolicy matching.
- tenant isolation.
- Subscription token revoke.
Edge Runtime tests must cover:
- request signing client.
- local identity loading.
- desired state version comparison.
- runtime config validate/apply.
- rollback.
- redaction.
Tenant Console tests must cover:
- API client error mapping.
- tenant context propagation.
- form validation for deployment token creation.
- node list/status rendering.
16. Deployment Model
MVP deployment:
Cloudflare Pages
apps/console
VPS or container host
services/control-plane
PostgreSQL
Redis
Customer or managed node host
orbitmesh
orbitmeshd
sing-box
Later:
- Kubernetes deployment for Control Plane.
- managed node pools.
- private enterprise deployment.
17. Non-Goals
Do not implement these in MVP:
- microservice split.
- Control Plane APIs in Next.js route handlers.
- direct database access from Tenant Console.
- RuntimeConfiguration as product source of truth.
- Kubernetes CRD implementation.
- multi-runtime abstraction beyond the sing-box adapter interface.