Guides / Declarative workflows

Declarative workflows

Bundle immutable JSON to connect an approved HTTPS GET to encrypted checkpointing and notifications. Runtime 0.2's bundled executor is Android-only. This path needs no application Kotlin, C++, JNI, receiver classes, or service implementations. Other platforms report unavailable; query oro:workflows.getSupport() before offering controls.

Package the definition#

Start with this oro.toml configuration, in addition to your app's normal identity and window settings:

[build]
copy_map = "copy-map.toml"

[workflows]
enabled = true
definitions = "workflows.json"

[foreground]
enabled = true

[permissions]
allow_notifications = true

Keep the definition in the project root. Include it and your app resources through copy-map.toml:

"src/index.html" = "index.html"
"src/main.js" = "main.js"
"workflows.json" = "workflows.json"

Create workflows.json using a fixed HTTPS endpoint you control. The following schema demonstrates a public read; example.invalid is deliberately non-resolving and must be replaced before packaging:

{
  "schema": 1,
  "workflows": [{
    "id": "public-read",
    "request": {
      "url": "https://example.invalid/public/alerts",
      "timeoutMs": 5000,
      "maximumResponseBytes": 65536,
      "cursorParameter": "after"
    },
    "response": {
      "items": "/alerts",
      "eventId": "/id",
      "checks": [
        { "pointer": "/accountRef", "equalsRef": "accountRef" },
        { "pointer": "/targetRef", "equalsRef": "targetRef" }
      ],
      "cursor": "/cursor"
    },
    "notification": {
      "title": "/title",
      "body": "/body",
      "channel": "public.workflow",
      "publicVersion": {
        "title": "Account update",
        "body": "Open the app to review it."
      }
    },
    "channels": [{
      "id": "public.workflow",
      "name": "Account updates",
      "importance": "low",
      "sound": "none"
    }]
  }]
}

The server response must match the declared account/target checks and JSON pointers. For example, its alerts array contains objects with id, accountRef, targetRef, title, and body; the optional top-level cursor supports the next read. The literal packaged publicVersion protects lock-screen content; it is not taken from the server response.

Definitions are bounded to 16 workflows in 64 KiB. The runtime loads them from APK assets, not a URL, mutable page, downloaded policy, or service worker. Requests have fixed GET policy, bounded timeouts and response sizes, and no redirects, request bodies, expressions, downloaded code, or queued mutations.

Bind from an explicit user action#

The following helper belongs in the trusted foreground page. Call it from the app's Start action after resolving the current server-authorized identity:

import workflows from 'oro:workflows'
import foreground from 'oro:foreground'

export async function startPublicRead({ authorityRef, accountRef, targetRef }) {
  const support = await workflows.getSupport()
  if (!support.available) throw new Error(support.reason || 'Workflow backend unavailable')
  const binding = await workflows.bind({
    workflow: 'public-read', authorityRef, accountRef, targetRef, userInitiated: true
  })
  const admitted = await workflows.run({ id: binding.id, userInitiated: true })
  return { binding, controller: await foreground.getStatus(admitted.id) }
}

Admission is not completion: inspect the finite controller to follow the outcome. This public-read example does not install a bearer credential. For authenticated reads, the definition names a fixed secure-storage source key. The app performs foreground sign-in and stores its approved read-only bearer, then calls importCredential and passes the returned opaque handle to bind. Workflow calls do not accept arbitrary passwords, tokens, or key aliases.

For user-selected self-hosted origins, bundle authority: "approved" and a fixed path, then obtain an authorityHandle through approveAuthority. Approval is scoped to the exact identity and definition revision. A gateway prefix additionally requires allowApprovedBasePath in the bundled request and getSupport().approvedBasePaths; the native consent dialog shows the complete fixed read URL. Local approval does not replace certificate trust or server IAM.

Schedule, monitor, or accept push hints#

Enable [background] deferred_enabled = true for workflows.schedule. Explicit monitoring additionally needs a bundled pollIntervalMs (5000–3600000), [foreground] continuous_enabled = true, and a truthful continuous_subtype. workflows.monitor uses the shared continuous controller and its visible Stop action; it does not automatically restart after process loss, boot, or force-stop.

For push, enable [push] enabled = true with provider = "unifiedpush". getPushOptions supplies workflow references for oro:push.register; the app still authenticates server enrollment and acknowledges the exact endpoint generation. Push payloads only hint that the runtime should read the fixed approved endpoint.

Retry and reconcile outcomes#

Check readRetryPolicy === 'bounded-read-backoff-v1': a cycle allows five GET attempts within 120 seconds, with delays of 1, 2, 4, and 8 seconds and the owning controller's deadline. Transport failures, timeouts, and HTTP 408/429/500/502/503/504 may retry. TLS/trust rejection, redirects, authorization failure, invalid/oversized responses, invalid scope, and local storage errors do not retry.

An explicitly running monitor waits at least 60 seconds after exhausting transient retries, or longer if its configured interval requires it. Push hints cannot bypass that cooldown. Scheduled execution has a separate bounded admission budget and can require explicit reconciliation/resume.

Only reads and response validation retry. Notification attempts are recorded before OS submission; interrupted submission stays unknown without automatic replay. getEvents and getResponses expose opaque outcomes and the exact notificationId, not response bodies or credentials. A notification response is a navigation hint: restore current account authorization before opening it.

Stop/revocation disables new local use and cancels owned work. Inspect drained before releasing handles. Reconcile unknown outcomes before releasing event records, because releaseEvents permits those IDs to become eligible again; cancellation alone does not release deduplication. Remote grants, transport registrations, and existing notifications need their own revocation, unregister, or cancellation calls.

See the pinned workflow reference and public fixture for authority approval, executor composition, correlation, retention limits, and exact-device qualification.

Source repository · Edit this page · View Markdown

Quick links

Type to search across the site. Use the up and down arrow keys to choose a result, Enter to open it, and Escape to close.