Skip to content

Application lifecycle and background execution

Campus treats workload lifetime as a user preference, not publisher authority. Packages declare interfaces and permissions; they do not declare background, trigger lists, or an always-on policy.

User-owned modes

Every installed workload has one lifecycle mode:

ModeBehavior
foregroundRuns while Campus has a fresh visible-interface lease. Once the interface closes and admitted work is quiescent, Campus stops it. Triggers cannot wake it.
automaticThe usual package default for workload apps. Visible interfaces and authorized triggers start it. Any foreground lease, admitted request, or renewable work lease keeps it alive. When the final liveness signal disappears, Campus starts the user-selected idle delay and then stops it.
always-onCampus continuously reconciles the workload to running. This is required for direct UDP services, which cannot authenticate and buffer a wake request while the workload is stopped.

Frontend-only apps are always foreground because they have no workload to run. Packages must provide complete defaults.lifecycle values; Campus copies them when installing and only fills newly introduced missing fields during catalog migration. App Settings remains authoritative. An automatic idle timeout may be set from 30 seconds through 24 hours. warmOnCampusOpen is valid for wakeable workloads and boots only the container after shell first paint. These settings are exposed through GET/PUT /v1/apps/:app-id/lifecycle.

campus.yaml must not contain background, and there is no background.run permission. Removing publisher policy avoids granting persistent compute merely by installing a package and lets the user apply one consistent policy to every workload.

What can wake an app

In automatic and always-on modes, Campus derives wake paths from interfaces the package declares and the user grants:

  • an authenticated MCP interface call may wake the exact target installation;
  • a user-created Campus schedule may wake its target and invoke one of that app's admitted MCP tools;
  • a request matching a granted public HTTP or WebSocket method, host, and path may request network wake after Gateway authorization;
  • opening the app creates a short foreground lease which the visible trusted shell renews.

There is no generic “wake app by ID” capability. The caller must already be eligible for the exact interface. Runtime receives the derived interface-call and, where applicable, network-request wake set in its current control-plane spec and rejects any other wake reason.

Setting a workload to foreground disables trigger wake. Disabling a schedule disables that schedule independently. Account-wide Runtime pause temporarily withdraws every route, schedule, and wake path without changing the saved per-app lifecycle choice.

Quiescence and work leases

Campus treats foreground presence, admitted requests, and work leases as liveness signals. An automatic workload becomes quiescent only after the final signal disappears; its configured idle timeout begins at that transition, not when the work was originally admitted. Campus stops a workload only when all of these are true:

  1. its foreground lease has expired;
  2. its mode and idle deadline permit shutdown;
  3. no authenticated Gateway request or stream is in flight;
  4. no CampusOS MCP invocation is in flight; and
  5. no unexpired app work lease exists.

Gateway increments an authorized-activity counter when a request or stream is admitted and decrements it only when the response, WebSocket, or SSE stream completes. App-originated Campus service traffic does not extend workload lifetime by itself.

Finite work which continues after its admitting request returns must acquire and renew an expiring lease:

js
const lease = await app.runtime().acquireWorkLease({
  reason: 'Finish media export',
  maxDurationSeconds: 120,
  renewEveryMilliseconds: 30000,
})

try {
  await finishExport()
} finally {
  await lease.release()
}

The same lease surface is available in a Node workload from any module through CampusServicesClient.fromEnv() in @campus/sdk; it consumes CAMPUS_API_URL and CAMPUS_GATEWAY_TOKEN. Omit renewEveryMilliseconds and call lease.renew() explicitly when a job wants checkpoint-controlled renewal rather than the SDK timer.

Leases are intrinsically scoped to the calling installation, require no manifest permission, work only in automatic mode, and expire after at most 24 hours even if the process crashes. Renewal sets a new bounded deadline from the current time; an expired lease cannot be resurrected. Releasing or allowing the final lease to expire begins the ordinary idle delay, so completing a long job never causes immediate shutdown. Leases are a liveness mechanism, not authorization: a lease cannot add an interface, start another app, change a schedule, or bypass account pause or billing admission.

There is no separate app-requested sleep marker. An app becomes eligible for automatic sleep by releasing its leases or stopping their renewal after it has persisted results, completed required responses, and sent completion notifications. This composes naturally across an agent graph: every agent that accepts asynchronous work owns and renews its own installation-scoped lease. A parent agent cannot force a child installation to remain alive.

Wake and shutdown sequence

mermaid
sequenceDiagram
  participant Source as Authorized source
  participant Campus as CampusOS
  participant Gateway as Installation Gateway
  participant Runtime as Campus Runtime
  participant App as App workload

  Source->>Campus: MCP call, schedule, or visible open
  Campus->>Runtime: ensureInstance (if automatic/always-on)
  Source->>Gateway: or granted public HTTP/WebSocket request
  Gateway->>Runtime: authorized wake request
  Runtime->>App: start or reuse workload
  Gateway->>Gateway: hold active-request counter
  App-->>Source: response or stream
  Gateway->>Gateway: release active-request counter
  Campus->>Runtime: stop after final signal disappears + idle delay

Failure behavior and limitations

  • Runtime, node, or billing admission failures fail closed; they do not relax interface authorization.
  • Concurrent wake requests converge on the same idempotent Runtime start, but callers still observe cold-start latency and may receive a temporary-unavailable response if startup fails.
  • Granted public routes can wake an automatic app from unsolicited authorized traffic. Users who do not want that cost should select foreground or revoke the route grant. Rate and spend limits remain separate resource-policy concerns.
  • Direct UDP ports cannot wake a stopped workload. Their settings surface warns that always-on is required; Campus does not silently override the user's selected mode.
  • A work lease protects liveness only until its deadline. Long-running systems should checkpoint and renew bounded units of work rather than acquiring an indefinite lease. A failed process naturally stops renewing, then sleeps after lease expiry and the configured idle delay.
  • CampusOS currently dispatches due schedules through one bounded scheduler worker. A slow run can delay later due runs; each invocation has a configured runtime limit and missed occurrences are not replayed.
  • Persistent volumes, external subscriptions, and retained network identity may still have costs after compute sleeps.

Software belongs to people. Campus gives it a durable place to run.