Docs/System Modules/App Runtime

App Runtime

An App is one package and one trust boundary containing any combination of UI, service, and job workloads.

Manifest v1Last updated July 17, 2026

App code is ordinary framework-neutral code. Lamarck standardizes the command boundary, durable APIs, runtime identity, and capability ceiling around it.

Package

apps/
└── personal-assistant/
    ├── .git/
    ├── manifest.json
    ├── package.json
    ├── package-lock.json
    ├── index.html
    ├── main.tsx
    └── src/…
  • The directory name is the App ID and must match manifest.id.
  • The ID and named workload IDs match ^[a-z0-9][a-z0-9-]*$.
  • The package chooses its own language, framework, build tool, and internal process topology.
  • Every declared command runs with the App package root as its working directory.

One package is the version, permission, and trust boundary. Splitting a UI and worker into separate Apps is appropriate only when they need independent permission ceilings or lifecycle.

Manifest

{
  "manifestVersion": 1,
  "id": "personal-assistant",
  "name": "Personal Assistant",
  "description": "Organizes personal requests, notes, and recurring assistant work.",
  "runtime": {
    "ui": {
      "command": ["npm", "run", "start"],
      "port": 3000
    },
    "services": {
      "inbox-watcher": {
        "command": ["npm", "run", "inbox-watcher"]
      }
    },
    "jobs": {
      "daily-review": {
        "command": ["npm", "run", "daily-review"]
      }
    }
  },
  "permissions": {
    "writes": {
      "docs": ["reviews/"],
      "tables": ["assistant_state"]
    }
  }
}

The parser is strict: unknown fields are rejected. The permissions.writes object and both of its permission arrays are required, and runtime must contain at least one actual workload.

FieldContract
descriptionRequired non-empty natural-language explanation of what the App is for.
runtime.*.commandNon-empty argv array. The Host executes it directly; there is no shell interpolation.
runtime.ui.portInteger 1–65535; the UI listens on this Capsule-internal port.
permissions.writes.docsExtra writable D1 exact IDs or prefixes ending in /.
permissions.writes.tablesConcrete D2 table names the App may mutate. Wildcards are invalid.
Not a workload kind

runtime.agents is rejected in manifest v1. An agent-like process is a service when continuously supervised or a job when invoked to completion.

Workloads

KindLifecycleUse it for
uiViewer-activated, Host-supervised process with one internal port and stable Host origin.Full-stack web UI, HTTP/WebSocket server, App-internal process tree.
serviceOne continuously supervised instance per named entry; liveness is process liveness.Workers, local queues, indexing loops, agent services.
jobA new process per invocation; exit completes the run.Event-triggered automation, scheduled ETL, bounded agent runs.

Schedules, Event triggers, retry policy, concurrency, cancellation, timeout, and run history are mutable Host state. They do not belong in manifest.json.

Runtime identity and lifecycle

The Host derives a stable source identity before starting the command:

app:personal-assistant:ui
app:personal-assistant:service:inbox-watcher
app:personal-assistant:job:daily-review

A workload cannot claim another identity. When it writes a D0 Event, Guard injects the active identity as source. Job run IDs are execution records; they do not change the stable provenance string.

Archiving moves the entire package, including Git history, to .lamarck/archived-apps/. It leaves the active registry but remains recoverable. Existing Timeline Events and durable D1/D2 state are not rewritten.

System API

import { system } from "@lamarck/system";

const result = await system.query(
  "SELECT * FROM assistant_state LIMIT 20"
);

await system.writeDoc(
  "reviews/2026-07-15",
  "# Daily review\n…"
);

await system.writeEvent({
  type: "assistant.review.completed",
  externalId: "2026-07-15",
  startedAt: Date.now(),
  payload: { document: "reviews/2026-07-15" }
});
MethodOperation
query(sql, params?)Read relational data in data.db. SELECT/WITH only.
resolveContentRef(ref)Resolve referenced D0 content when full text is explicitly needed.
mutate(sql, params?)Run one INSERT, UPDATE, or DELETE on a permitted D2 table.
transaction(statements)Atomically execute a serializable list of permitted mutations.
writeDoc / deleteDocMutate D1 inside the App's Document grants.
writeEvent(event)Append a D0 Event. Omit source; the runtime injects it.

Apps can query all relational data in data.db in v1. Read access is not granted table by table. Mutation remains limited to declared Tables and Document paths. DDL goes through the separate lamarck promote / demote structural workflow.

Sandbox and permissions

All workloads in one App share a logical Capsule and the same manifest permission ceiling. The sandbox filesystem is scratch or materialization, not authoritative storage.

Available through capabilityNot ambiently available
Read-only relational queriesdata.db or system.db handle/path
Declared D1 and D2 mutationsHost filesystem or another App's scratch state
Host-mediated effects explicitly exposed to the AppReusable credentials, devices, Host APIs, unrestricted network

Because read queries may contain personal data, any future capability that can export results is itself an explicit effect. It is not implied by query access.

Next moduleConnector Runtime