@ossy/platform
v3.9.0
Published
Ossy application server runtime
Readme
@ossy/platform
Express-based application server runtime for the Ossy platform. It reads the build manifest produced by @ossy/app build and wires up pages, API routes, tasks, actions, integrations, aggregates, and startup hooks — all without any server-side configuration.
What it does
At startup @ossy/platform:
- Loads
build/manifest.jsonproduced by@ossy/app build. - Registers the manifest's toggleable packages with
@ossy/workspaces/entitlements(setEnableablePackages) so workspace service toggles and action entitlement checks use scoped npm names (@ossy/booking, not slug-only keys). - Registers and runs all startup hooks (
*.startup.js) in order. - Connects all integrations (
*.integration.js) by callingconnect({ env }). - Registers all tasks (
*.task.js) withTaskServiceand starts the cron scheduler. - Registers all schemas (
*.schema.js) withregisterSchema. - Rebuilds all aggregates (
*.aggregate.js) from the event store. - Registers all actions (
*.action.js) withActionService. - Mounts MCP at
POST /mcpand servesGET /capabilities.json. - Starts an Express server that routes requests to pages (
*.page.jsx) and API handlers (*.api.js). - Auto-mounts every action at
POST /actions({ action, payload }).
Page SSR fills the app:content slot (see PlatformShell and resolve-app-slots in @ossy/app). App chrome uses namespaced keys such as app:header mapped from export const slots in *.layout.jsx.
Quick start
# In your app directory
npm install @ossy/app @ossy/platform
# Build the app
npx app build
# Start the server
npx platform startOr programmatically:
import { startServer } from '@ossy/platform'
const { port, close } = await startServer({
cwd: process.cwd(), // defaults to process.cwd()
buildDir: 'build', // defaults to 'build'
port: 3000, // also reads --port / PORT env var
})Server configuration
The server reads configuration from:
--port/-pCLI flag, or thePORTenvironment variable (default3000).build/manifest.json— produced by@ossy/app build.process.env— used by integrations for their credentials and by startup hooks.
Platform integrations are boot-time and process-global via IntegrationService. Per-workspace credentials are a separate design — see WORKSPACE-INTEGRATION-SECRETS.md (SPEC only; not implemented).
Optional environment variables used by the platform itself:
| Variable | Description |
|---|---|
| DB_URL | MongoDB connection string. Required for tasks and aggregates. |
| API_URL + OSSY_API_KEY | Optional HTTP bot SDK for tasks. When unset, tasks get an in-process SDK that calls ActionService / storage in the same process (local app-test and same-server changestream). |
| PORT | HTTP port. |
Health check
Both startServer (website / app images) and startRuntime (CMS multi-tenant image) expose an unauthenticated liveness probe:
| Method | Path | Response |
|---|---|---|
| GET / HEAD | /health | 200 with { ok: true, status: "ok", service } |
The route is mounted before auth and before CMS site loading, so ALB target groups and ECS container health checks can use path /health without cookies, API tokens, or a resolvable hostname.
The runtime image Dockerfile sets PORT / OSSY_SERVICE_NAME and a Docker HEALTHCHECK via WORKDIR-root docker-healthcheck.js (same PORT + /health + 4s abort shape as ECS inline probes from @ossy/deployment-tools).
Exported API
import {
startServer,
loadManifest,
resolveEntryUrl,
ConfigService,
ActionService,
StorageClient,
S3Client,
LocalStorageClient,
getSystemSchemas,
schemaForWorkspace,
validateSchemasForImport,
} from '@ossy/platform'ActionService
Registry for *.action.js command handlers.
import { ActionService } from '@ossy/platform'
// Invoke an action from server-side code (bypasses HTTP)
const result = await ActionService.invoke('orders/create', {
payload: { ... },
req: { userId: 'user-123', workspaceId: 'ws-456' },
})
// Look up a registered action
const action = ActionService.get('orders/create') // { id, access, run } | null
// List all registered actions
const all = ActionService.all()getSystemSchemas
Returns all system schemas registered from *.schema.js files.
import { getSystemSchemas } from '@ossy/platform'
const schemas = getSystemSchemas()
// [{ id: '@ossy/tool/doc', name: 'Tool Doc', fields: [...] }, ...]Primitives
The platform is built around file conventions called primitives. Each primitive is a file with a specific naming pattern that the build pipeline auto-discovers.
→ See PRIMITIVES.md for the complete reference.
| Primitive | Pattern | Purpose |
|---|---|---|
| Page | *.page.jsx | Routable UI (SSR + hydration) |
| API | *.api.js | HTTP endpoint (any method) |
| Task | *.task.js | Event-driven or scheduled async work |
| Action | *.action.js | Named intent, auto-exposed at POST /actions |
| Integration | *.integration.js | Third-party client connected at startup |
| Email | *.email.jsx | Transactional React email template |
| Component | *.component.jsx | Injectable UI fragment |
| Resource | *.schema.js | Custom document-type schema |
| Aggregate | *.aggregate.js | Event-sourced domain object |
| Startup | *.startup.js | One-time boot hook |
Request lifecycle
Incoming request
│
├─ POST /actions ──► ActionService.invoke(actionId) ──► TaskService.invoke(taskIdFromActionId) ──► task.run(...)
│
├─ Match API route ──► api.handle(req, res)
│
└─ Match page route ──► page.render(props) ──► HTML responseRequests that do not match an API route or page route receive 404 Not Found.
Related packages
| Package | Purpose |
|---|---|
| @ossy/app | Build pipeline — discovers primitives, bundles them, writes manifest.json |
| @ossy/event-store | Event sourcing primitives (Aggregate, EventStore) |
| @ossy/email | Email renderer and email.integration.js |
| @ossy/observability | Structured logger and metrics |
| @ossy/router | URL matching used by the platform server |
