Skip to content

Platform

The Orbital Platform is a composable application engine built on NATS JetStream. It provides the foundation for building applications declaratively through Ørbs.

Think of Orbital like Kubernetes: a platform that starts empty but gains capabilities as handlers register themselves. You then compose applications by applying Ørb specs.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Orbital Platform (monolith) β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Core Engine (internal/platform/core) β”‚ β”‚
β”‚ β”‚ - Ørb/Module/Schema/Entry primitives β”‚ β”‚
β”‚ β”‚ - Handler registry + dynamic routing β”‚ β”‚
β”‚ β”‚ - Auto-CRUD from schemas β”‚ β”‚
β”‚ β”‚ - JetStream event sourcing β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Built-in Handlers β”‚ β”‚
β”‚ β”‚ - auth.* (passkey, sessions) β”‚ β”‚
β”‚ β”‚ - games.* (launch, sync, RTP) β”‚ β”‚
β”‚ β”‚ - payments.* (deposit, withdraw) β”‚ β”‚
β”‚ β”‚ - accounts.* (register, profile) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ NATS RPC (optional)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β–Ό β–Ό β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Custom β”‚ β”‚ Special β”‚ β”‚ 3rd Pty β”‚ ← Extensions (via NATS)
β”‚ Handler β”‚ β”‚Provider β”‚ β”‚ Integr. β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Monolith-first architecture:

  • All core capabilities are built into Orbital
  • Single deployment, simple operations
  • Extensions can register via NATS RPC without code changes
  • Ørbs are domain DATA - Schemas, entries, content you manage in backoffice
  • Handlers are OPERATIONS - Custom logic, integrations, flows
  • Ørbs declare what they USE - Services are orb-agnostic, routes are orb-prefixed
  • Event-sourced - Complete revision history via JetStream
  • Schema-driven - Declarative structure, auto-generated UI
  • Loosely coupled - Handlers self-register, orbs compose them
KindPurposeExample
OrbApplication containergames, payments, access
ModuleFeature modulecatalog, providers, settings
SchemaData structuregame, provider, transaction
EntryContent instanceAuto-generated ID
TypePurpose
CONTENTTranslatable content entries (games, pages, banners)
CONFIGConfiguration data, market-specific but not translatable
RULESBusiness rules (evaluation engine)
TRANSACTIONSTransaction records and audit logs
SETTINGSApplication and user preferences

Orbs declare which service handlers they use via uses. Routes are generated at /api/{orbId}/{handler}:

id: casino
uses:
- service: auth
handlers: [signup, login, logout]
- service: games
handlers: [launch, getSession]
- service: payments
handlers: [deposit, withdraw]

Generated routes:

POST /api/casino/signup β†’ auth.signup (orbId: casino)
POST /api/casino/login β†’ auth.login (orbId: casino)
POST /api/casino/launch β†’ games.launch (orbId: casino)
POST /api/casino/deposit β†’ payments.deposit (orbId: casino)

The handler receives orbId in the request and can:

  • Read orb-specific config (session TTL, allowed methods)
  • Write to orb-specific streams (sessions, audit logs)
  • Apply orb-specific business rules

Orbs define domain data with schemas and field attributes:

id: games
uses:
- service: games
handlers: [launch, getSession, syncCatalog]
modules:
- id: catalog
type: CONTENT
schemas:
- id: game
fields:
- name: title
type: string
translatable: true
- name: providerId
type: reference
ref: games/providers/provider
- name: rtp
type: number
- name: enabled
type: boolean
marketSpecific: true
- id: providers
type: CONFIG
schemas:
- id: provider
fields:
- name: name
type: string
- name: apiKey
type: string
sensitive: true
AttributePurpose
translatableEntry can be translated per language
marketSpecificEntry can vary per market
sensitiveEncrypted at rest, masked in UI
refLinks to another schema for dropdowns
enumAllowed values list
uniqueMust be unique across entries
defaultDefault value

The platform exposes three API groups:

PrefixPurpose
/api/admin/Platform Management API (orbs, modules, schemas, entries)
/api/{orbId}/Runtime API (dynamic routes from published orbs)
/api/openapi, /api/discoveryAPI Discovery (public)

These IDs cannot be used as orb names (they conflict with platform routes):

  • admin - Platform management
  • openapi - OpenAPI spec
  • discovery - API discovery
  • health - Health checks
  • metrics - Prometheus/OTEL

Administrative endpoints for managing platform resources. Requires platform_admin role.

The Management API follows a document-oriented design:

  • Structure (orbs/modules/schemas) = single document, atomic updates
  • Content (entries) = granular CRUD, high volume
/api/admin/
β”œβ”€β”€ registry[/{service}] # Handler registry discovery
β”œβ”€β”€ routes # Active route listing
β”œβ”€β”€ orbs/ # Orb CRUD (document-oriented)
β”‚ └── {orbId}/
β”‚ β”œβ”€β”€ publish # Publish orb
β”‚ β”œβ”€β”€ revisions[/{v}] # Revision history
β”‚ └── entries/ # Entry CRUD (granular)
β”‚ └── {entryId}/
β”‚ β”œβ”€β”€ publish # Publish entry
β”‚ └── revisions # Entry revisions

Structure Endpoints (Document-Oriented)

MethodPathDescription
GET/api/admin/orbsList all orbs
POST/api/admin/orbsCreate orb (full document with modules/schemas)
GET/api/admin/orbs/{id}Get orb with all modules and schemas
PUT/api/admin/orbs/{id}Update orb (full document, server diffs)
DELETE/api/admin/orbs/{id}Delete orb and all content
POST/api/admin/orbs/{id}/publishPublish orb
GET/api/admin/orbs/{id}/revisionsList revisions
GET/api/admin/orbs/{id}/revisions/{v}Get specific revision

Content Endpoints (Granular)

MethodPathDescription
GET/api/admin/orbs/{id}/entriesList entries (filter: ?module=&schema=)
POST/api/admin/orbs/{id}/entriesCreate entry (module/schema in body)
GET/api/admin/orbs/{id}/entries/{eid}Get entry
PUT/api/admin/orbs/{id}/entries/{eid}Update entry
DELETE/api/admin/orbs/{id}/entries/{eid}Delete entry
POST/api/admin/orbs/{id}/entries/{eid}/publishPublish entry
GET/api/admin/orbs/{id}/entries/{eid}/revisionsEntry revisions

Discovery Endpoints

MethodPathDescription
GET/api/admin/registryList all registered handlers
GET/api/admin/registry/{service}Get handlers for specific service
GET/api/admin/routesList active routes

Routes generated dynamically from published orbs:

/api/
β”œβ”€β”€ openapi # OpenAPI 3.0 spec (all routes)
β”œβ”€β”€ discovery # API discovery info
β”œβ”€β”€ {orbId}/
β”‚ β”œβ”€β”€ call/{handler} # Service handlers (from uses)
β”‚ └── {moduleId}/
β”‚ └── {schemaId}/ # Auto-CRUD (multi-schema modules)
β”‚ └── {id}/ # Entry operations

Each CONTENT/CONFIG module automatically gets CRUD routes:

MethodPathDescription
GET/api/{orbId}/{moduleId}/{schemaId}List entries
GET/api/{orbId}/{moduleId}/{schemaId}/{id}Get entry
POST/api/{orbId}/{moduleId}/{schemaId}Create entry
PUT/api/{orbId}/{moduleId}/{schemaId}/{id}Update entry
DELETE/api/{orbId}/{moduleId}/{schemaId}/{id}Delete entry
POST/api/{orbId}/{moduleId}/{schemaId}/{id}/publishPublish entry
POST/api/{orbId}/{moduleId}/{schemaId}/{id}/archiveArchive entry

Smart routing for single-schema modules: When a module has only one schema, the schemaId is omitted:

GET /api/games/catalog/game # Multi-schema: includes schemaId
GET /api/accounts/operators # Single-schema: schemaId omitted
MethodPathDescription
GET/api/openapiOpenAPI 3.0 spec (all Management + Runtime routes)
GET/api/discoveryQuick overview of available orbs, modules, schemas

Handlers self-register and are auto-discovered. They’re exposed as API endpoints automatically.

TypeDescriptionUse Case
LocalIn-process handler functionBuilt-in capabilities
NATSRemote handler via NATS RPCExtensions, microservices
HTTPNative HTTP handlerWebAuthn, OAuth flows
// Local handler (in-process)
core.RegisterLocal("games.launch", launchHandler, core.HandlerSpec{
Description: "Launch a game session",
Auth: core.AuthRequired,
})
// NATS RPC handler (remote service)
core.RegisterNATS("payments.deposit", "orbital.payments.deposit", core.HandlerSpec{
Description: "Process a deposit",
Auth: core.AuthRequired,
})
Terminal window
# CLI
orbital registry list # List all registered handlers
orbital registry get games # Handlers for games service
# API
GET /api/admin/registry # All handlers
GET /api/admin/registry/games # Filter by service
GET /api/openapi # OpenAPI 3.0 spec
GET /api/discovery # Quick API overview

Orbital is a monolith with extension points. External services can register handlers via NATS:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” NATS RPC β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Orbital β”‚ ◄───────────────► β”‚ Payment Svc β”‚
β”‚ (platform) β”‚ β”‚ (extends) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ β”‚
└─── orbital.payments.* β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

To extend Orbital:

  1. Create a service that connects to NATS
  2. Subscribe to handler subjects (e.g., orbital.payments.process)
  3. Register with Orbital’s registry (or let Orbital discover via NATS Micro)
  4. Handlers are automatically available to orbs

This allows:

  • Custom integrations without modifying Orbital
  • Third-party provider integrations
  • Specialized processing (ML, analytics)
SAVED β†’ PUBLISHED β†’ UNPUBLISHED β†’ ARCHIVED
  • SAVED: Draft state, not visible
  • PUBLISHED: Live, routes active
  • UNPUBLISHED: Offline, routes removed
  • ARCHIVED: Soft-deleted
Terminal window
orbital serve # Start platform
orbital serve --legacy # Start with legacy orchestrators
orbital orb ls # List orbs
orbital orb get myapp # Get orb
orbital orb apply -f x.yaml # Apply config
orbital orb pub myapp # Publish
internal/platform/core/
β”œβ”€β”€ item.go # Core Item type (Orb/Module/Schema/Entry) + ReservedOrbIDs
β”œβ”€β”€ manager.go # NATS JetStream storage
β”œβ”€β”€ router.go # Dynamic route engine
β”œβ”€β”€ registry.go # Handler registry
β”œβ”€β”€ reconciler.go # Stream/route synchronization
β”œβ”€β”€ service.go # Business logic layer
β”œβ”€β”€ middleware.go # Middleware types and registration
β”œβ”€β”€ middleware_builtin.go # logging middleware
β”œβ”€β”€ system.go # Built-in system handlers
β”œβ”€β”€ mgmt_handlers.go # Management API router (/api/admin/)
β”œβ”€β”€ handlers_orbs.go # Orb CRUD handlers
β”œβ”€β”€ handlers_modules.go # Module CRUD handlers
β”œβ”€β”€ handlers_schemas.go # Schema CRUD handlers
β”œβ”€β”€ handlers_entries.go # Entry CRUD handlers
β”œβ”€β”€ runtime_handlers.go # Runtime API handlers (/api/{orbId}/)
β”œβ”€β”€ openapi.go # OpenAPI spec generation + discovery
└── rpc.go # RPC request/response types

The core engine (internal/platform/core) has minimal dependencies:

PackagePurpose
internal/platform/gravityNATS query engine
internal/platform/httpxHTTP utilities
internal/platform/loggerStructured logging

No database dependency - all state is in NATS JetStream.

  • Ørb/Module/Schema/Entry primitives
  • Handler registry (Local, NATS, HTTP)
  • Service binding (uses) with route generation
  • Auto-CRUD routes from schemas
  • JetStream event sourcing with revisions
  • Middleware system (JWT, roles, logging)
  • CLI commands (serve, orb ls/get/apply/pub)
  • Management API (/api/admin/)
  • Runtime API (/api/{orbId}/)
  • API Discovery (/api/openapi, /api/discovery)
  • Reserved orb IDs validation
  • Field attributes (translatable, marketSpecific, sensitive, ref, etc.)
  • ChangeLog with diff generation
  • Scheduler service for validFrom/validTo
  • Rule evaluation engine for RULES modules
  • NATS Micro auto-discovery for extensions

See TODO.md for details.

The orbital stream stores platform-level configuration and definitions:

Stream NamePurposeSubject PatternExamples
orbitalPlatform configuration & definitionsorbital.*orbital.orbs.*
orbital.definitions.messages.*
orbital.definitions.modules.*

Each Ørb gets its own stream with ø-prefix naming:

Orb NameStream NameSubject PatternExample Subjects
accountsΓΈaccountsaccounts.{module}.{role}.{schema}.{id}accounts.content.admin.users.abc123
accounts.config.owner.plans.def456
paymentsΓΈpaymentspayments.{module}.{role}.{schema}.{id}payments.transactions.user.invoices.xyz789
payments.config.admin.gateways.pay001
gamesΓΈgamesgames.{module}.{role}.{schema}.{id}games.content.public.catalog.game123
games.rules.admin.leaderboards.lb001
game-platformΓΈgame-platformgame-platform.{module}.{role}.{schema}.{id}game-platform.content.user.saves.save456
game-platform.settings.admin.features.feat789

Subject Components:

  • {module}: Module type - content, config, rules, transactions, settings
  • {role}: Access role - owner, admin, user, public
  • {schema}: Schema name (kebab-case)
  • {id}: Entry ID (xid format)