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, oritemResultProcessor. - Resources are reusable JavaScript files resolved at package time from configured resource directories.
Primary provenance:
docs/src/llm/processed/docs/workflow.mddocs/src/llm/processed/docs/patterns.mddocs/src/llm/preprocessed/bootstrap-schema.mddocs/src/llm/preprocessed/workflow-schema.mddocs/src/llm/processed/introduction/getting-started-full.md
Start by checking feasibility
Before writing configuration, map the requirement to Opscotch’s actual execution model.
- Choose a supported trigger path:
httptimertcpfileWatcherrunOncedeploymentAccess
- 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
- Identify the outputs:
- HTTP response
- metrics
- logs
- OTEL diagnostics
- file writes
- outbound HTTP
- 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.mddocs/src/llm/processed/introduction/problem-to-plan.md
Bootstrap authoring
The bootstrap root is an array. Each record requires:
deploymentIdremoteConfiguration
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
remoteConfigurationTimeoutmust be less thanfrequencywhenfrequency > 0frequency: 0means load once onlystartupPrioritycontrols 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
allowListexists, calls must match one of its method and path rules authenticationHost: truerestricts 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
patternsprovide a second restriction layer- operational docs treat
READandLISTas 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 bodycontext.setProperty("status_code", "...")sets the response status
Deployment access
Use allowDeploymentAccess to permit cross-deployment calls.
Schema-backed rules:
access: "call"requiresdeploymentIdaccess: "receive"withanyDeployment: trueforbidsdeploymentIdaccess: "receive"withoutanyDeployment: truerequiresdeploymentId
Licensing, package trust, and keys
- Runtime licensing is configured in bootstrap
licensing.licenseHostandlicensing.licenseHostPoolId. - Package trust is configured in bootstrap
packaging, includingpackageId, signer requirements, andpackagerIdentities. - Cryptographic material is configured in bootstrap
keys. - Key purpose and type must match schema constraints:
signauthenticatedanonymoussymmetric
Primary provenance:
docs/src/llm/preprocessed/bootstrap-schema.mddocs/src/llm/processed/administrating/agent.mddocs/src/llm/processed/blog/2025-02-04-allow-external-host-access.mddocs/src/llm/processed/blog/2025-02-03-allow-file-access.mddocs/src/llm/processed/blog/2025-02-03-allow-http-server-access.mddocs/src/llm/processed/administrating/licensing.mddocs/src/llm/processed/administrating/cryptography.md
Workflow authoring
The workflow root is an object with required workflows. It may also include:
datamessagesdefaultStepPropertiescomment
Each workflow requires:
namesteps
Each step requires:
stepId
Useful step fields:
typetriggerdatasingleThreadedhttpTimeoutpersistenceFileorpersistence- processor hooks such as
urlGenerator,resultsProcessor,payloadGenerator,authenticationProcessor
Processor structure
A processor can be:
- a single
script - a single
resource - a composite
processorsarray
Rules:
scriptandresourceare the single-source forms- if
scriptisnull,resourceis required - do not combine
processorswithscriptorresource - 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:
timerfor scheduled workhttpfor inbound HTTPfileWatcherfor file changesrunOncefor startup-only initializationdeploymentAccessfor cross-deployment entry points
Author conservatively against the schema:
- always include
trigger.http.method, even where older prose examples omit it - treat
runOnceastrigger: { "runOnce": true } - use
runOnceNoTracefor startup trace suppression, not a generic trigger-levelnoTrace
Step concurrency
By default, a step may overlap with itself.
Current documented singleThreaded values:
nonereturn
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-onlyJavascriptStateContext
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:
authenticationProcessorbelongs 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.mddocs/src/llm/processed/docs/workflow.mddocs/src/llm/processed/docs/patterns.mddocs/src/llm/processed/blog/2025-02-06-single-threaded.mddocs/src/llm/processed/blog/2025-11-08-send-to-step.mddocs/src/llm/processed/blog/2025-11-08-context-errors.mddocs/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, andresultsProcessor. - Inbound HTTP uses bootstrap listener IDs plus
trigger.http. - Multipart upload support is off by default and turns on only when
trigger.http.multiPartUploadByteLimitis set. - In multipart mode, the expected field names are
bodyandstream. context.getBody()returns a string.context.getStream()returns aByteReader.
File watching
- File watching is built on bootstrap file-access permissions rather than a separate permission system.
trigger.fileWatcherrequiresbootstrapFileIdandeventSplitter.splitAtEndmatters 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.
ByteReaderis 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(...), anderrored(...)alongside normal workflow error recording. - Treat trigger-level trace suppression conservatively because corpus sources disagree on full
noTracecoverage.
Primary provenance:
docs/src/llm/processed/blog/2025-02-04-allow-external-host-access.mddocs/src/llm/processed/blog/2025-02-07-file-watcher.mddocs/src/llm/processed/blog/2025-11-08-multipart-http-uploads.mddocs/src/llm/processed/blog/2025-11-08-byte-manipulation.mddocs/src/llm/processed/blog/2025-11-08-crypto-context.mddocs/src/llm/processed/blog/2025-02-07-queue.mddocs/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:
- create
testrunner.config.json - set license, resource dirs, and test dirs
- run
opscotch-testrunner ... - start the agent with the generated test bootstrap arguments
- 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: 1for 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.packageIdwith the packagedpackageId - 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
licenseHostandlicenseHostPoolId ownerKeyHexmust 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.mddocs/src/llm/processed/introduction/getting-started-full.mddocs/src/llm/processed/administrating/packaging.mddocs/src/llm/processed/blog/2026-03-24-packaging.mddocs/src/llm/processed/administrating/licensing.mddocs/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
authenticationProcessorand downstreamscripted-authhelpers only. - Use
doccontracts 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
persistenceRootinstead 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-0025indocs/src/llm/operations/unknowns/blog/2025-02-03-allow-http-server-access-unknowns.md- Blog prose and some examples treat
trigger.http.methodas optional, but the workflow schema requires it. Write it explicitly.
- Blog prose and some examples treat
UNKNOWN-OTEL-0002indocs/src/llm/operations/unknowns/blog/2025-11-08-otel-unknowns.md- The OTEL HTTP example also omits
method, repeating the same schema conflict.
- The OTEL HTTP example also omits
UNKNOWN-0001indocs/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.
- The tutorial prose says “every 10 minutes” while the example uses
UNKNOWN-0002indocs/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-0007indocs/src/llm/operations/unknowns/docs/patterns-unknowns.md- Same-key
datamerging is resolved for primitive overwrite versus additive object/array merge, but type-mismatch edge cases are still undocumented.
- Same-key
UNKNOWN-PAT-0008indocs/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-0002andUNKNOWN-SENDSTEP-0003indocs/src/llm/operations/unknowns/blog/2025-11-08-send-to-step-unknowns.mdsendToStepAndForget(...)concurrency limits, ordering, startup-failure visibility, and durability guarantees are not fully documented.
UNKNOWN-FW-0002andUNKNOWN-FW-0003indocs/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-001throughUNKNOWN-004indocs/src/llm/operations/unknowns/blog/2025-11-10-doc-unknowns.md- The
docpreprocessor is practically useful, but the corpus still lacks a full reference contract and richer runtime semantics.
- The
UNKNOWN-PKG-0001throughUNKNOWN-PKG-0003indocs/src/llm/operations/unknowns/administrating/packaging-unknowns.md- Default package encryption semantics, encrypted-string wire-format grammar, and manifest
sourcePathgrammar are still not fully specified.
- Default package encryption semantics, encrypted-string wire-format grammar, and manifest
Provenance summary
Highest-weight sources for this skill:
docs/src/llm/processed/docs/workflow.mddocs/src/llm/processed/docs/patterns.mddocs/src/llm/processed/introduction/getting-started-full.mddocs/src/llm/processed/administrating/agent.mddocs/src/llm/processed/administrating/testing.mddocs/src/llm/processed/administrating/packaging.mddocs/src/llm/processed/administrating/licensing.mddocs/src/llm/processed/administrating/cryptography.mddocs/src/llm/preprocessed/bootstrap-schema.mddocs/src/llm/preprocessed/workflow-schema.mddocs/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/