Skip to main content
Version: Next

Opscotch LLM Skill

Implementation-focused guidance for engineers authoring bootstraps, workflows, resources, tests, packages, and runtime operations in Opscotch.

Scope

This skill is synthesized from the processed corpus under docs/src/llm/processed/, the reconciled unknown files under docs/src/llm/operations/unknowns/, the registry state, the flattened bootstrap/workflow schemas, the split API lookup corpus under docs/static/llm/, and the human-authored workflow explanation in docs/docs/workflow.md.

Use this file for:

  • bootstrap versus workflow design decisions
  • workflow structure and step composition
  • host, file, listener, and deployment permission setup
  • processor authoring patterns
  • testing, packaging, cryptography, and licensing decisions

Do not use this file as the full API reference. For exact method signatures and lookup-style contracts, consult:

  • /llm/apireference-index.json
  • /llm/apireference.json
  • /llm/apireference-authenticated-index.json
  • /llm/apireference-authenticated.json

Core mental model

Opscotch has a hard separation between administrator-controlled bootstrap configuration and remotely updateable workflow logic.

  • Bootstrap decides what the runtime is allowed to do: where workflow config comes from, which external hosts may be called, which files may be read or written, which HTTP listeners may be bound, which other deployments may be called, where persistence lives, which keys and package-signing identities are trusted, and how licensing is obtained.
  • Workflow configuration decides how the allowed capabilities are used: workflows, steps, triggers, processors, persistence declarations, data overrides, and step-to-step flow.
  • A workflow is made of steps.
  • A step is the execution boundary.
  • A processor is JavaScript or a resource-backed JavaScript fragment attached to a step lifecycle point such as urlGenerator, resultsProcessor, payloadGenerator, authenticationProcessor, splitGenerator, or itemResultProcessor.
  • Resources are reusable JavaScript files resolved at package time from configured resource directories.

Primary provenance:

  • docs/src/llm/processed/docs/workflow.md
  • docs/src/llm/processed/docs/patterns.md
  • docs/src/llm/preprocessed/bootstrap-schema.md
  • docs/src/llm/preprocessed/workflow-schema.md
  • docs/src/llm/processed/introduction/getting-started-full.md

Start by checking feasibility

Before writing configuration, map the requirement to Opscotch’s actual execution model.

  1. Choose a supported trigger path:
    • http
    • timer
    • tcp
    • fileWatcher
    • runOnce
    • deploymentAccess
  2. Identify how required inputs enter the workflow:
    • trigger payload
    • bootstrap or host data
    • file reads through context.files(...)
    • outbound HTTP calls to bootstrap-allowed hosts
  3. Identify the outputs:
    • HTTP response
    • metrics
    • logs
    • OTEL diagnostics
    • file writes
    • outbound HTTP
  4. Check processor constraints:
    • plain synchronous JavaScript only
    • no Node.js built-ins
    • no require, import, Promise, await, timers, or callbacks

If a solution depends on unsupported triggers, arbitrary filesystem access, arbitrary outbound networking, external libraries, or in-processor async behavior, redesign around bootstrap permissions and step-to-step orchestration.

Primary provenance:

  • docs/src/llm/processed/docs/workflow.md
  • docs/src/llm/processed/introduction/problem-to-plan.md

Bootstrap authoring

The bootstrap root is an array. Each record requires:

  • deploymentId
  • remoteConfiguration

Common bootstrap responsibilities:

  • point the agent at a raw workflow JSON file or packaged .oapp
  • define allowExternalHostAccess
  • define allowFileAccess
  • define allowHttpServerAccess
  • define allowDeploymentAccess
  • set persistenceRoot
  • configure licensing
  • configure package trust under packaging
  • preload cryptographic keys under keys
  • provide deployment-level data

Important bootstrap rules:

  • bootstrap configuration is immutable after startup; changing it requires restart
  • bootstrap records are schema-closed; undeclared properties are not allowed
  • remoteConfigurationTimeout must be less than frequency when frequency > 0
  • frequency: 0 means load once only
  • startupPriority controls activation order; lower loads first

Use bootstrap for environment-specific or operator-controlled concerns. Use workflow config for remotely updateable business logic.

External hosts

Use allowExternalHostAccess to permit outbound HTTP.

Each host entry is operator-controlled and referenced by id from workflow JavaScript:

  • context.setHttpMethod(...)
  • context.setUrl(hostId, path)
  • context.setBody(...)

Key constraints:

  • workflows can call only bootstrap-defined hosts
  • workflows can extend the configured path, not replace the base host
  • once allowList exists, calls must match one of its method and path rules
  • authenticationHost: true restricts access to authentication flows

File access

Use allowFileAccess to permit file operations.

Key points:

  • workflows call context.files("<id>")
  • file access is rooted at the configured directoryOrFile
  • workflows may append subpaths under that root, not replace the root
  • patterns provide a second restriction layer
  • operational docs treat READ and LIST as the practical prerequisite for file watching, even though the schema cannot express that requirement

HTTP listeners

Use allowHttpServerAccess to permit inbound HTTP or packaged static serving.

Key points:

  • until a workflow binds a step to the listener, the listener returns 404
  • the workflow trigger uses trigger.http.server
  • overlapping listener bindings are resolved by match specificity; source guidance says longest matching regex wins, otherwise the last bound trigger can win
  • context.setMessage(...) sets the response body
  • context.setProperty("status_code", "...") sets the response status

Deployment access

Use allowDeploymentAccess to permit cross-deployment calls.

Schema-backed rules:

  • access: "call" requires deploymentId
  • access: "receive" with anyDeployment: true forbids deploymentId
  • access: "receive" without anyDeployment: true requires deploymentId

Licensing, package trust, and keys

  • Runtime licensing is configured in bootstrap licensing.licenseHost and licensing.licenseHostPoolId.
  • Package trust is configured in bootstrap packaging, including packageId, signer requirements, and packagerIdentities.
  • Cryptographic material is configured in bootstrap keys.
  • Key purpose and type must match schema constraints:
    • sign
    • authenticated
    • anonymous
    • symmetric

Primary provenance:

  • docs/src/llm/preprocessed/bootstrap-schema.md
  • docs/src/llm/processed/administrating/agent.md
  • docs/src/llm/processed/blog/2025-02-04-allow-external-host-access.md
  • docs/src/llm/processed/blog/2025-02-03-allow-file-access.md
  • docs/src/llm/processed/blog/2025-02-03-allow-http-server-access.md
  • docs/src/llm/processed/administrating/licensing.md
  • docs/src/llm/processed/administrating/cryptography.md

Workflow authoring

The workflow root is an object with required workflows. It may also include:

  • data
  • messages
  • defaultStepProperties
  • comment

Each workflow requires:

  • name
  • steps

Each step requires:

  • stepId

Useful step fields:

  • type
  • trigger
  • data
  • singleThreaded
  • httpTimeout
  • persistenceFile or persistence
  • processor hooks such as urlGenerator, resultsProcessor, payloadGenerator, authenticationProcessor

Processor structure

A processor can be:

  • a single script
  • a single resource
  • a composite processors array

Rules:

  • script and resource are the single-source forms
  • if script is null, resource is required
  • do not combine processors with script or resource
  • composite processors execute in order

Prefer resources for reusable logic and inline script for small local behavior.

Data merging

data can appear at:

  • bootstrap
  • host
  • workflow
  • step
  • processor

Resolved corpus rule:

  • merge order is from broader to more specific
  • last merged wins
  • primitives overwrite
  • objects and arrays merge additively

Persistence

Persistence is step-scoped state, not a free global store.

Use:

  • context.setPersistedItem(key, value)
  • context.getPersistedItem(key)

Operational guidance:

  • prefer bootstrap persistenceRoot
  • ensure the persistence directory exists and survives restart or redeploy if state continuity matters
  • treat stored values as strings unless you explicitly serialize structured data

Trigger guidance

Use:

  • timer for scheduled work
  • http for inbound HTTP
  • fileWatcher for file changes
  • runOnce for startup-only initialization
  • deploymentAccess for cross-deployment entry points

Author conservatively against the schema:

  • always include trigger.http.method, even where older prose examples omit it
  • treat runOnce as trigger: { "runOnce": true }
  • use runOnceNoTrace for startup trace suppression, not a generic trigger-level noTrace

Step concurrency

By default, a step may overlap with itself.

Current documented singleThreaded values:

  • none
  • return

Use return when order matters more than throughput, especially for queue-consuming steps.

Step-to-step flow

Blocking call:

  • sendToStep(...) waits and returns a read-only JavascriptStateContext

Fire-and-forget:

  • sendToStepAndForget(...) returns immediately

Use blocking forwarding when downstream output affects the current decision. Use fire-and-forget only when you can tolerate weak observability and undocumented scheduling semantics.

Error handling

Opscotch exposes distinct error channels:

  • addUserError(...)
  • addSystemError(...)

Useful checks:

  • isErrored()
  • hasUserErrors()
  • getUserErrors()
  • hasSystemErrors()
  • getSystemErrors()
  • getAllErrors()
  • getFirstError(...)

Use user errors for operator or caller-fixable problems. Use system errors for code, configuration, or unexpected internal failures.

Authentication flow

There are two processor contexts:

  • normal JavascriptContext
  • restricted AuthenticationJavascriptContext

Rules synthesized across workflow and patterns docs:

  • authenticationProcessor belongs to the step making the outbound HTTP call
  • it runs immediately before that outbound call
  • any step reached from that authentication flow must be scripted-auth
  • auth flow logic can access restricted authentication data and step auth properties
  • auth flow cannot call non-authentication steps

Documentation and validation preprocessor

Version 3.1.0 introduces doc for processor-side contracts:

  • .description(...)
  • .inSchema(...)
  • .dataSchema(...)
  • .outSchema(...)
  • .asUserErrors()
  • .run(() => { ... })

Use it to validate incoming body, merged data, and outgoing body at workflow-load and processor-run boundaries. Keep doc.run(...) as the wrapper around the full processor body.

Primary provenance:

  • docs/src/llm/preprocessed/workflow-schema.md
  • docs/src/llm/processed/docs/workflow.md
  • docs/src/llm/processed/docs/patterns.md
  • docs/src/llm/processed/blog/2025-02-06-single-threaded.md
  • docs/src/llm/processed/blog/2025-11-08-send-to-step.md
  • docs/src/llm/processed/blog/2025-11-08-context-errors.md
  • docs/src/llm/processed/blog/2025-11-10-doc.md

Capability notes that matter in real implementations

HTTP request and response flows

  • Outbound HTTP uses bootstrap host IDs and step processors such as urlGenerator, payloadGenerator, authenticationProcessor, and resultsProcessor.
  • Inbound HTTP uses bootstrap listener IDs plus trigger.http.
  • Multipart upload support is off by default and turns on only when trigger.http.multiPartUploadByteLimit is set.
  • In multipart mode, the expected field names are body and stream.
  • context.getBody() returns a string.
  • context.getStream() returns a ByteReader.

File watching

  • File watching is built on bootstrap file-access permissions rather than a separate permission system.
  • trigger.fileWatcher requires bootstrapFileId and eventSplitter.
  • splitAtEnd matters for whether the splitter stays with the preceding or following record.
  • The corpus does not fully document rotation, late-created files, or partial-record semantics, so avoid assuming tailer-grade behavior beyond the documented examples.

Byte and crypto work

  • Byte handling is handle-based; the runtime owns underlying memory.
  • Buffers are freed automatically at step end unless explicitly released earlier.
  • ByteReader is forward-only.
  • context.crypto() provides signing, verification, public-key encryption, anonymous encryption, symmetric encryption, hashing, and random bytes.
  • Keep key purpose aligned with operation and prefer bootstrap-managed keys or packager-generated keys.

Queue work

  • context.queue() exposes a persistent FIFO queue.
  • push(...) appends.
  • take(N) is at-most semantics, not exact-count semantics.
  • take(...) does not block waiting for disk-backed data to be paged back into memory.
  • Queue consumers that require ordering should usually pair queue reads with singleThreaded: "return".

OTEL

  • OTEL runtime enablement uses OPSCOTCH_OTEL_PROPERTIES.
  • Processor-level trace enrichment uses context.diagnostic().
  • Use setAttribute(...), event(...), and errored(...) alongside normal workflow error recording.
  • Treat trigger-level trace suppression conservatively because corpus sources disagree on full noTrace coverage.

Primary provenance:

  • docs/src/llm/processed/blog/2025-02-04-allow-external-host-access.md
  • docs/src/llm/processed/blog/2025-02-07-file-watcher.md
  • docs/src/llm/processed/blog/2025-11-08-multipart-http-uploads.md
  • docs/src/llm/processed/blog/2025-11-08-byte-manipulation.md
  • docs/src/llm/processed/blog/2025-11-08-crypto-context.md
  • docs/src/llm/processed/blog/2025-02-07-queue.md
  • docs/src/llm/processed/blog/2025-11-08-otel.md

Testing, packaging, and deployment

Testing

Opscotch testing is mocked integration testing, not unit-only validation.

Workflow:

  1. create testrunner.config.json
  2. set license, resource dirs, and test dirs
  3. run opscotch-testrunner ...
  4. start the agent with the generated test bootstrap arguments
  5. let the harness mock endpoints and assert on calls, metrics, and logs

Operational guidance:

  • resource and test directories are interpreted relative to the working directory where the test runner is started
  • the corpus strongly suggests concurrency: 1 for deterministic examples, but the default is not explicitly documented

Packaging

Use the packager when you need:

  • resource inlining
  • workflow inspection
  • package signatures
  • authenticated encryption
  • bootstrap encryption
  • authenticated string encryption
  • manifest-based workflow assembly

Important package decisions:

  • match bootstrap packaging.packageId with the packaged packageId
  • use signatures when runtime signer verification matters
  • use authenticated encryption when recipient-restricted cryptographic protection matters
  • treat minimal packaging as convenient but not equivalent to end-to-end cryptographic assurance

Licensing

Current runtime licensing model:

  • hierarchical chain ROOT -> ISSUER -> VENDOR -> ORG -> PLATFORM -> RUNTIME
  • runtime bootstrap points at a licensing app through licenseHost and licenseHostPoolId
  • ownerKeyHex must be retained from the first successful issuance response
  • environment-specific licenses must match production versus non-production runtime mode
  • the admin licensing doc states the embedded-license legacy path is planned for removal in 3.2.0

Primary provenance:

  • docs/src/llm/processed/administrating/testing.md
  • docs/src/llm/processed/introduction/getting-started-full.md
  • docs/src/llm/processed/administrating/packaging.md
  • docs/src/llm/processed/blog/2026-03-24-packaging.md
  • docs/src/llm/processed/administrating/licensing.md
  • docs/src/llm/processed/blog/2026-03-24-licensing.md

Authoring defaults that are safest today

  • Put environment-specific access and trust decisions in bootstrap, not workflow.
  • Put reusable JavaScript in resources and package them from explicit resource directories.
  • Always include trigger.http.method.
  • Use singleThreaded: "return" for order-sensitive polling or queue-consumer steps.
  • Use blocking sendToStep(...) unless you explicitly design observability for fire-and-forget paths.
  • Keep auth logic in authenticationProcessor and downstream scripted-auth helpers only.
  • Use doc contracts for processor inputs and outputs where workflow interfaces matter.
  • Prefer packaged workflows for controlled deployment; prefer raw workflow JSON only when you want live file-based reload behavior.
  • Use bootstrap persistenceRoot instead of scattering per-step persistence paths unless there is a clear reason not to.
  • Treat the split API corpus as authoritative for method signatures and return types.

Important unresolved unknowns

These are still material to correctness or operator expectations and should remain visible.

  • UNKNOWN-0025 in docs/src/llm/operations/unknowns/blog/2025-02-03-allow-http-server-access-unknowns.md
    • Blog prose and some examples treat trigger.http.method as optional, but the workflow schema requires it. Write it explicitly.
  • UNKNOWN-OTEL-0002 in docs/src/llm/operations/unknowns/blog/2025-11-08-otel-unknowns.md
    • The OTEL HTTP example also omits method, repeating the same schema conflict.
  • UNKNOWN-0001 in docs/src/llm/operations/unknowns/introduction/getting-started-full-unknowns.md
    • The tutorial prose says “every 10 minutes” while the example uses period: 60000, which is 60 seconds if time values are milliseconds.
  • UNKNOWN-0002 in docs/src/llm/operations/unknowns/introduction/getting-started-full-unknowns.md
    • Tutorial “test bootstrap” examples are object fragments, while the formal bootstrap schema root is an array of deployment records.
  • UNKNOWN-PAT-0007 in docs/src/llm/operations/unknowns/docs/patterns-unknowns.md
    • Same-key data merging is resolved for primitive overwrite versus additive object/array merge, but type-mismatch edge cases are still undocumented.
  • UNKNOWN-PAT-0008 in docs/src/llm/operations/unknowns/docs/patterns-unknowns.md
    • Authentication-flow state visibility is documented only partially; multi-request and credential-caching semantics remain unclear.
  • UNKNOWN-SENDSTEP-0002 and UNKNOWN-SENDSTEP-0003 in docs/src/llm/operations/unknowns/blog/2025-11-08-send-to-step-unknowns.md
    • sendToStepAndForget(...) concurrency limits, ordering, startup-failure visibility, and durability guarantees are not fully documented.
  • UNKNOWN-FW-0002 and UNKNOWN-FW-0003 in docs/src/llm/operations/unknowns/blog/2025-02-07-file-watcher-unknowns.md
    • File-watcher behavior for rotation, late-created files, batching, and partial records is not fully specified.
  • UNKNOWN-001 through UNKNOWN-004 in docs/src/llm/operations/unknowns/blog/2025-11-10-doc-unknowns.md
    • The doc preprocessor is practically useful, but the corpus still lacks a full reference contract and richer runtime semantics.
  • UNKNOWN-PKG-0001 through UNKNOWN-PKG-0003 in docs/src/llm/operations/unknowns/administrating/packaging-unknowns.md
    • Default package encryption semantics, encrypted-string wire-format grammar, and manifest sourcePath grammar are still not fully specified.

Provenance summary

Highest-weight sources for this skill:

  • docs/src/llm/processed/docs/workflow.md
  • docs/src/llm/processed/docs/patterns.md
  • docs/src/llm/processed/introduction/getting-started-full.md
  • docs/src/llm/processed/administrating/agent.md
  • docs/src/llm/processed/administrating/testing.md
  • docs/src/llm/processed/administrating/packaging.md
  • docs/src/llm/processed/administrating/licensing.md
  • docs/src/llm/processed/administrating/cryptography.md
  • docs/src/llm/preprocessed/bootstrap-schema.md
  • docs/src/llm/preprocessed/workflow-schema.md
  • docs/docs/workflow.md

Reconciled unknown state was preserved from:

  • docs/src/llm/operations/registry/unknowns.md
  • per-file unknown files under docs/src/llm/operations/unknowns/