Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ Use these as starting points when you want:
- `reference/` - canonical, production-adjacent repo examples

Every example contract declares `metadata.ota.minimum_version`. That floor is the oldest Ota
version the example is intended to run with; examples using the current v1.6.26 authority and
execution surfaces require Ota v1.6.26 or later. Ota refuses a contract whose minimum exceeds the
installed binary before validation, setup, or task execution.
version the example is intended to run with, so use each contract's declared floor rather than
assuming one repository-wide version. Ota refuses a contract whose minimum exceeds the installed
binary before validation, setup, or task execution.

## Choose by problem

Expand Down Expand Up @@ -158,6 +158,7 @@ Use the public reference for the governing model, then copy the nearest example:
| Adopt Ota in an existing repo | [`reference/adoption-flow`](reference/adoption-flow) | Flagship Java/Maven adoption with services, task prerequisites, docs, and release companions. |
| Model a production-shaped team repo | [`reference/canonical-team-repo`](reference/canonical-team-repo) | Env policy, reusable surfaces, probe-backed readiness, and release-only requirements. |
| Add CI, container, or remote execution | [`ci`](ci) or [`execution`](execution) | Provider patterns and execution-boundary examples. |
| Keep host-only setup out of container CI | [`execution/container/node-service`](execution/container/node-service) | Uses `execution.modes.native.depends_on` so aggregate verification follows only the selected backend graph. |
| Bootstrap several repositories together | [`workspace/adoption-flow`](workspace/adoption-flow) | First-week workspace adoption and multi-repo readiness. |

### Setup, toolchains, and hydration
Expand Down
3 changes: 2 additions & 1 deletion execution/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Use this theme when ota should run repo tasks through a backend boundary instead
- how to keep container execution explicit
- how to keep remote execution explicit
- how to preserve one repo contract across multiple execution locations
- how mode-specific dependencies keep host-only preparation out of container execution

## Folders

Expand All @@ -55,7 +56,7 @@ Use this theme when ota should run repo tasks through a backend boundary instead
5. `local-topology/shared-local-backend-fulfillment/README.md`
6. `local-topology/shared-local-backend-environment/README.md`
7. `container/template/README.md`
8. `container/node-service/README.md`
8. `container/node-service/README.md` for mode-specific dependency and selected-graph behavior
9. `remote/README.md`
10. `remote/template/README.md`
11. `remote/ssh-node-service/README.md`
Expand Down
18 changes: 16 additions & 2 deletions execution/container/node-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ A copyable starting point for a Node service that runs in a container context, k
- keeps local and CI runtime behavior aligned
- makes the execution boundary obvious
- gives `ota doctor` and `ota run` a stable path
- keeps `run: pnpm dev` simple while ota owns host URL resolution
- keeps the structured `pnpm dev` launch explicit while ota owns host URL resolution
- keeps one canonical task intent (`start`) while still supporting `--mode native` and mode-specific lifecycle
- keeps native-only local preparation out of the container verification graph through
`execution.modes.native.depends_on`
- helps a team standardize Node setup without turning the README into a shell-script graveyard

## Use when
Expand All @@ -54,6 +56,8 @@ ota run setup
ota run start
# open the URL ota prints (same value as OTA_PUBLIC_URL)
ota run start --mode native
ota run verify --agent
ota run verify --mode native --agent
```

## Projection contract
Expand All @@ -70,14 +74,24 @@ ota run start --mode native

Ota binds the repo source into the container as usual, but overlays `node_modules` with an engine-managed named volume under the execution context so platform-specific binaries stay compatible with the container image.

`tasks.setup`, `tasks.test`, and the aggregate `tasks.verify` show the selected dependency
boundary. Container verification selects `setup -> test -> verify`; native verification selects
`setup:host:local -> setup -> test -> verify`. The native-only local preparation does not make
the container aggregate unavailable and does not enter its selected graph identity. In a real
repository that prerequisite may materialize `.env.local`, a host tool shim, or another
host-specific input; the dependency belongs only on the mode that consumes it, and container
execution does not read or report an optional env source owned only by that native branch.

## Included app

This folder includes a minimal runnable app:

- `package.json` with `dev` and `test` scripts
- `src/server.mjs` that serves `http` and `metrics`, and prints `OTA_PUBLIC_URL` plus `OTA_PUBLIC_URL_METRICS`

Run `ota run test` if you want to verify the example task chain without starting the long-running dev server.
Run `ota run verify --agent` if you want to exercise the complete container task chain without
starting the long-running dev server. Use `ota run verify --mode native --agent` to exercise the
host-specific preparation path.
Run `ota run clean:start` if you want the same app intent with container `lifecycle: ephemeral`.

## Reset Ota-managed state
Expand Down
1 change: 1 addition & 0 deletions execution/container/node-service/native-ready.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
prepared-by=ota
71 changes: 60 additions & 11 deletions execution/container/node-service/ota.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,19 @@
version: 1
metadata:
ota:
minimum_version: "1.6.23"
minimum_version: "1.6.28"

project:
name: node-service-container
description: Node service template with Ota container mode for setup, dev, and test
toolchains:
node:
version: "22"
version: ">=22"
package_managers:
pnpm: "10.0.0"
fulfillment:
source: corepack
mode: run
execution:
default_context: app
contexts:
Expand All @@ -54,8 +57,27 @@ execution:
isolated_paths:
- node_modules
tasks:
setup:host:local:
description: Create a host-only local preparation marker when native execution is selected.
category: setup
execution:
default_mode: native
action:
kind: copy_if_missing
from: native-ready.example
to: .local/native-ready
safe_for_agent: true
internal: true
setup:
context: app
execution:
default_mode: container
modes:
native:
context: host
depends_on:
- setup:host:local
container:
context: app
prepare:
kind: dependency_hydration
medium: package_dependencies
Expand Down Expand Up @@ -85,15 +107,21 @@ tasks:
HOSTNAME: 127.0.0.1
PORT: "3000"
METRICS_PORT: "9090"
run: pnpm dev
launch:
kind: command
exe: pnpm
args: [dev]
container:
context: app
lifecycle: persistent
env:
HOSTNAME: 0.0.0.0
PORT: "3000"
METRICS_PORT: "9090"
run: pnpm dev
launch:
kind: command
exe: pnpm
args: [dev]
runtime:
kind: service
listeners:
Expand Down Expand Up @@ -134,15 +162,21 @@ tasks:
HOSTNAME: 127.0.0.1
PORT: "3000"
METRICS_PORT: "9090"
run: pnpm dev
launch:
kind: command
exe: pnpm
args: [dev]
container:
context: app
lifecycle: ephemeral
env:
HOSTNAME: 0.0.0.0
PORT: "3000"
METRICS_PORT: "9090"
run: pnpm dev
launch:
kind: command
exe: pnpm
args: [dev]
runtime:
kind: service
listeners:
Expand Down Expand Up @@ -179,7 +213,13 @@ tasks:
Use `ota run clean:start` when you want an ephemeral container lifecycle.
Ota injects `OTA_PUBLIC_URL` before the process starts and prints the same URL after startup.
test:
context: app
execution:
default_mode: container
modes:
native:
context: host
container:
context: app
command:
exe: pnpm
args:
Expand All @@ -191,17 +231,26 @@ tasks:
- node
notes: |
Example: `ota run test` before merging from the container context.
verify:
description: Run the complete verification closure in one selected execution mode.
aggregate:
tasks:
- test
safe_for_agent: true
agent:
entrypoint: setup
default_task: test
default_task: verify
safe_tasks:
- setup:host:local
- setup
- start
- clean:start
- test
- verify
verify_after_changes:
- test
- verify
writable_paths:
- .local
- node_modules
- src
- tests
Expand All @@ -213,7 +262,7 @@ agent:
note: Only install ota if it is missing and installation is approved.
source:
kind: version
version: v1.6.23
version: v1.6.28
notes: |
Use Ota first for setup, validation, and task execution.
Treat Ota as the default workflow when the repo runs inside a container.
Expand Down
9 changes: 9 additions & 0 deletions execution/container/node-service/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions ota.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ agent:
ota:
note: Only install ota if it is missing and installation is approved.
source:
kind: git_rev
rev: 6538cc074a78b3a0471f976a415238d13307a699
kind: version
version: v1.6.28
notes: |
Use Ota first for validation and task execution.
Treat the README files as the adoption surface and ota.yaml as the contract.
Expand Down
19 changes: 15 additions & 4 deletions reference/runtime-proof-evidence/scripts/observe_marker.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,26 @@

import json
import os
import time
import urllib.error
import urllib.request
from pathlib import Path

with urllib.request.urlopen("http://127.0.0.1:8081/marker", timeout=3) as response:
marker = response.read().decode()
marker = None
for _ in range(30):
try:
with urllib.request.urlopen("http://127.0.0.1:8081/marker", timeout=3) as response:
if response.status == 200:
candidate = response.read().decode()
if candidate:
marker = candidate
break
except (OSError, urllib.error.URLError):
pass
time.sleep(0.2)

if not marker:
raise RuntimeError("dependency did not return the proof marker")
raise RuntimeError("dependency did not return the proof marker within the bounded retry window")

Path(os.environ["OTA_PROOF_ATTESTATION_FILE"]).write_text(
json.dumps(
Expand All @@ -21,4 +33,3 @@
}
)
)

18 changes: 14 additions & 4 deletions reference/runtime-proof-evidence/scripts/produce_marker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@
# Licensed under the Apache License, Version 2.0. See LICENSE for the full license text.

import os
import time
import urllib.error
import urllib.request

marker = os.environ["OTA_PROOF_DEPENDENCY_MARKER"].encode()
request = urllib.request.Request("http://127.0.0.1:8081/marker", data=marker, method="POST")
with urllib.request.urlopen(request, timeout=3) as response:
if response.status != 204:
raise RuntimeError(f"unexpected dependency response: {response.status}")

last_error = None
for _ in range(30):
try:
with urllib.request.urlopen(request, timeout=3) as response:
if response.status != 204:
raise RuntimeError(f"unexpected dependency response: {response.status}")
break
except (OSError, urllib.error.URLError) as error:
last_error = error
time.sleep(0.2)
else:
raise RuntimeError(f"dependency did not accept the proof marker: {last_error}")
4 changes: 3 additions & 1 deletion reference/task-prepare-package-hydration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ This example shows the shipped node package-manager `tasks.<name>.prepare` slice
- `source.kind: node_package_manager`
- `source.manager: yarn`
- `source.mode: install`
- `source.frozen_lockfile: true` for strict `yarn install --immutable`
- `source.frozen_lockfile: true` for strict modern Yarn `yarn install --immutable`; starting with
the Ota 1.6.28 implementation, a Yarn 1 contract can also declare
`source.yarn_release: classic` for `yarn install --frozen-lockfile`
- explicit `requirements.toolchains: [node]`
- explicit `effects.writes`
- explicit `effects.network_kind: dependency_hydration`
Expand Down
Loading