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
12 changes: 9 additions & 3 deletions UPSTREAM.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
"runtime/descriptor_unix.go": "df41b6867e9840933f186c93b6bc61861e7ea5252a365455686ea0414bfa0044",
"runtime/descriptor_windows.go": "2d9ffefe3af0154fa8042de6b67460d4e86dd3f4cdd9e986f180f7d0c535c9a5",
"runtime/request.go": "198c44fd6c547022a15b6d0d48e4d0130fa8afb687994365115576e4d874550d",
"runtime/request_test.go": "86d8a2feb4ec72e8ed675b9567da2d1f5d628950eeec907c10b9cc1675aa1904",
"runtime/request_test.go": "7e1cafab3de6432af4ab60999c83e8e81c1a7e7c985e256e56cec2f24ce154cb",
"runtime/runtime.go": "36fdf4643c04d058fef380333a0a3942913d1dd60e910b893dc134da4b65d6bf",
"runtime/runtime_test.go": "afd78d122af20148bf30d0db873ff002544189df0dfec0f6167b8cf5cd0d42b1",
"runtime/strict_test.go": "d2eb2b78563733e3279289d8b6d17a4276e831ed3c78f8b3ec9d6706992ffaec",
Expand All @@ -91,6 +91,8 @@
"runtime/transport_test.go": "9b69f590f1e258470adea249b3ac6d4a00f1001f10bb08dfa7b56c6e2d6709ae",
"schema/schema.go": "fd5c3b76979c88aeed75fadb7e3c94abcad62e067d77595f021fb422a60f2211",
"sdk/csharp/Pitot/Types.cs": "3fecc1eabca6e3ad218c73741d185f815d868a618d1e7ed3b9320eeb6942977d",
"sdk/csharp/README.md": "dce102e6176e1a0ee8858fcd784bd9420a9603fb7f2079445e88f4b1e78df45d",
"sdk/java/README.md": "a5c3a6b350098473d7513126db2accd5b790128f6dcf5454d0547cc8284621ce",
"sdk/java/src/main/java/com/operatorstack/pitot/Action.java": "2d7974afc2f687921c5e8009077f7b812a8973cdb174de5020cb73f41e3c6570",
"sdk/java/src/main/java/com/operatorstack/pitot/BoundaryFault.java": "926c93d9a77b8ef1d94f4674e73e1b8c28aa19082e8f0261077be95ea975abb8",
"sdk/java/src/main/java/com/operatorstack/pitot/Content.java": "a430934623b0edfdc3a96f8b17da3deb1502b29e534c18fb580b4cd672b60f71",
Expand All @@ -102,16 +104,20 @@
"sdk/java/src/main/java/com/operatorstack/pitot/Observation.java": "179982beed89b31cc0a8218c46d8f1bfe6d9c7b1ecb7da82fbc26924d5051df7",
"sdk/java/src/main/java/com/operatorstack/pitot/Types.java": "26f8722e6b61faaa093e5a8b92388d3f93a2228026dc04b4b68d10340874a135",
"sdk/java/src/main/java/com/operatorstack/pitot/Usage.java": "bbddf4cb0fb7550ac14817600d9f233e68bfd84b72f73d5fbbf9a6cce6e3253d",
"sdk/python/README.md": "c49c15c48bbc064a4596815a94710c9f5270b38216c6b796cbd6fb38cf90fa34",
"sdk/python/pitot/__init__.py": "9cab11b333536f167d4e7bb6089ef00488690ec7b5cd5701f08e8bbb13cef0f7",
"sdk/python/pitot/runner.py": "8d4537ffc3aee2ea22b1d691559ac1eb1e95521df9ad5bf241ee6ac54c9925b1",
"sdk/python/pitot/types.py": "c9a7221f1ad6627f152f26148155d3c74f249c52262c73a515251f469e8eb4ad",
"sdk/python/pyproject.toml": "b7990a7a4d2a3db874a5f8c6f9e09673bf928936b64cf0bd4d4a1727aae3305d",
"sdk/runner.go": "e9a8db96d3cf6df7ea7e661e6755650f97e580970995dfe881b4268db0c3f832",
"sdk/rust/.gitignore": "6c135f35993002b30f7bdae3d88e923bbf33e4a25faa35dde452f6ab6fd4a6b2",
"sdk/rust/Cargo.toml": "1c42ed7ff351ad54ab87c29bbbef96a7bd574b4109ca12eb84d3f8daf42dcc77",
"sdk/rust/README.md": "a548821ca40287e4d17ffb12cd844f68ffc5d0f4358d3459f7bdafc8a55a0b2e",
"sdk/rust/src/lib.rs": "fd2dcb9bf9df47fb58e52e4e94136f95867616d941ba66b4b324413d1c5b1777",
"sdk/rust/src/runner.rs": "168eb3f926aed4b86aa27ad66ab605326974b6a345b07a35126d4b66e0968b33",
"sdk/rust/src/types.rs": "82cd5a5ec12effa6688e8b9b6028bbef67ec43f24d9fd9d19b4235934061ffb8",
"sdk/tests/test_runners.py": "9e86a03b53b108b70204e797b4c65b3d157c6765543a9bb9d7bb892daaf4d8c4",
"sdk/tests/test_runners.py": "28af8d9c235afe9c61e1d6e5e9b70ffe7054e36ec04310daaba4fd7488bda0e0",
"sdk/typescript/README.md": "5fc46f917ef30e944e1b20efae04d0a53ccc60fb868d143e44b04bcad684a51c",
"sdk/typescript/package.json": "747b5bab8df073d39b9ef92f28b6b7b43eb00bc127c0647cda4e4003b3db7295",
"sdk/typescript/src/index.ts": "bddb336bc120ecd979de0288e432ed2b41749d39f11c697f019af5e54c2e9fba",
"sdk/typescript/src/pitot.ts": "9c243824cbb7edc54b1e125abfd828bf2ed77e7151a0bd5c5d4f63e81ca9b00c",
Expand Down Expand Up @@ -151,7 +157,7 @@
"generator": "operatorstack/pitot:project",
"schema_version": 1,
"source": {
"commit": "b6b7e44f9b2c48ba6a48ef373adc624610582c57",
"commit": "ef3ef32beeb743ca17afe3bd74706798e3e72c04",
"path": "labs/15-pitot",
"repository": "operatorstack/intelligence-flow"
}
Expand Down
6 changes: 3 additions & 3 deletions runtime/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func TestNewControlRequestFillsEnvelopeAndMarshalsPayload(t *testing.T) {
Actor string `json:"actor"`
Operation string `json:"operation"`
}
req, err := NewControlRequest("interlock.decide", body{Actor: "publisher", Operation: "artifact.publish"})
req, err := NewControlRequest("interlock.effect", body{Actor: "publisher", Operation: "artifact.publish"})
if err != nil {
t.Fatalf("NewControlRequest: %v", err)
}
Expand All @@ -22,8 +22,8 @@ func TestNewControlRequestFillsEnvelopeAndMarshalsPayload(t *testing.T) {
if req.Type != schema.TypeControlRequested {
t.Errorf("type = %q, want %q", req.Type, schema.TypeControlRequested)
}
if req.Kind != "interlock.decide" {
t.Errorf("kind = %q, want interlock.decide", req.Kind)
if req.Kind != "interlock.effect" {
t.Errorf("kind = %q, want interlock.effect", req.Kind)
}
if req.ActionID == "" {
t.Error("ActionID was not minted")
Expand Down
21 changes: 21 additions & 0 deletions sdk/csharp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Pitot C# SDK — preview (not yet runnable)

**Status: types-only preview. Not a shipping SDK.** `Pitot/Types.cs` holds the
generated C# data types for the Pitot wire protocol, emitted by
[`scripts/generate_types.sh`](../../../scripts/generate_types.sh) from the same
schema as every other binding. They let you *read the shapes* — they do **not** yet
give you a runnable Controller or Consumer.

What is **not** here yet, and what a first-class SDK requires:

- no build manifest (`.csproj` / solution), so the package does not build;
- no `runner` (the stdio JSON-Lines Controller/Consumer loop the other SDKs ship);
- no entry in the cross-language conformance suite
([`../tests/test_runners.py`](../tests/test_runners.py)), which is what proves a
binding's wire behavior is identical to the others;
- not covered by the version-lockstep gate — it carries no release version.

The first-class, runnable SDKs are **Python**, **TypeScript**, **Go**, and
**Rust**. Use one of those to build against Pitot today; see each SDK's `README.md`
for a quickstart. This preview will graduate only once it clears the same bar: a
runner, a conformance entry, a build, and version lockstep.
21 changes: 21 additions & 0 deletions sdk/java/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Pitot Java SDK — preview (not yet runnable)

**Status: types-only preview. Not a shipping SDK.** These files are the generated
Java data types for the Pitot wire protocol (`com.operatorstack.pitot.*`), emitted
by [`scripts/generate_types.sh`](../../../scripts/generate_types.sh) from the same
schema as every other binding. They let you *read the shapes* — they do **not** yet
give you a runnable Controller or Consumer.

What is **not** here yet, and what a first-class SDK requires:

- no build manifest (`pom.xml` / `build.gradle`), so the package does not build;
- no `runner` (the stdio JSON-Lines Controller/Consumer loop the other SDKs ship);
- no entry in the cross-language conformance suite
([`../tests/test_runners.py`](../tests/test_runners.py)), which is what proves a
binding's wire behavior is identical to the others;
- not covered by the version-lockstep gate — it carries no release version.

The first-class, runnable SDKs are **Python**, **TypeScript**, **Go**, and
**Rust**. Use one of those to build against Pitot today; see each SDK's `README.md`
for a quickstart. This preview will graduate only once it clears the same bar: a
runner, a conformance entry, a build, and version lockstep.
65 changes: 65 additions & 0 deletions sdk/python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Pitot Python SDK

The Python binding for Pitot: typed wire types plus a tiny stdio runner for
building a **Controller** (decides `control.requested` → allow/deny) or a passive
**Consumer** (reads the observed action/event stream). It speaks the same
JSON-Lines protocol as every other Pitot SDK — proven identical by the
cross-language conformance suite ([`../tests/test_runners.py`](../tests/test_runners.py)).

## Install

Pitot SDKs install from the project's own registry through the CLI front door,
pinned to the CLI's version — never public PyPI:

```bash
pitot install python # writes .pitot/registry + operatorstack-pitot==<version>
```

`pitot init --language python` scaffolds a complete, runnable project and registers
it as a tenant fragment; the snippets below are the core of what it writes.

## Build a Controller

A Controller receives a typed `ControlRequested` and returns an `Outcome`. Return
`allow(...)` or `deny(...)`; the runner serializes one `control.response` per line,
in request order.

```python
from pitot.runner import run_controller, allow, deny
from pitot.types import ControlRequested

def handler(req: ControlRequested):
# decide from req.data, req.kind, etc.
if req.kind == "shell" and "rm -rf" in str(req.data):
return deny("blocked a destructive shell command")
return allow("approved")

if __name__ == "__main__":
run_controller("my-controller", handler)
```

## Build a Consumer

A Consumer passively reads the event stream. Keep stdout clean — a Consumer must
never write to stdout (the conformance suite enforces this); log to stderr.

```python
import sys
from pitot.runner import run_consumer
from pitot.types import Event

def handler(event: Event):
print("observed", event.type, file=sys.stderr)

if __name__ == "__main__":
run_consumer(handler)
```

## Run it behind an agent

```bash
pitot dev --host claude -- claude -p "…" # discovers your fragment, starts the controller
```

See the repository [README](../../../public-readme-preview/README.md) for the full
runtime, host-hook wiring, and the enforcement boundary.
5 changes: 5 additions & 0 deletions sdk/rust/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Rust build artifacts — the crate is a library; a resolved lockfile and the
# compiled target/ tree are produced by `cargo build`/`cargo test` (locally and
# in the conformance CI job) and are never part of the published SDK.
/target
Cargo.lock
60 changes: 60 additions & 0 deletions sdk/rust/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Pitot Rust SDK

The Rust binding for Pitot: typed wire types plus a tiny stdio runner for building a
**Controller** (decides `control.requested` → allow/deny) or a passive **Consumer**
(reads the observed action/event stream). It speaks the same JSON-Lines protocol as
every other Pitot SDK — proven identical by the cross-language conformance suite
([`../tests/test_runners.py`](../tests/test_runners.py)).

## Add the dependency

The crate is `pitot`. During local development, depend on it by path; a released
version installs from the project registry pinned to the CLI's version.

```toml
[dependencies]
pitot = { path = "path/to/sdk/rust" } # or the pinned registry version
```

## Build a Controller

A Controller receives a typed `ControlRequested` and returns an `Outcome`. Return
`allow(...)` or `deny(...)`; the runner serializes one `control.response` per line,
in request order.

```rust
use pitot::{run_controller, allow, deny, ControlRequested, Outcome};

fn handler(req: ControlRequested) -> Outcome {
if req.kind == "shell" {
return deny(Some("blocked a shell command".to_string()));
}
allow(Some("approved".to_string()))
}

fn main() {
run_controller("my-controller", Box::new(handler));
}
```

## Build a Consumer

A Consumer passively reads the event stream. Keep stdout clean — a Consumer must
never write to stdout (the conformance suite enforces this); log to stderr. Note the
event's protocol `type` field is `event_type` in Rust (it is `#[serde(rename =
"type")]`, since `type` is a reserved word).

```rust
use pitot::{run_consumer, Event};

fn handler(event: Event) {
eprintln!("observed {}", event.event_type);
}

fn main() {
run_consumer(Box::new(handler));
}
```

See the repository [README](../../../public-readme-preview/README.md) for the full
runtime, host-hook wiring, and the enforcement boundary.
2 changes: 1 addition & 1 deletion sdk/tests/test_runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def handler(event: Event):
RUST_CONSUMER = """use pitot::{run_consumer, Event};

fn handler(event: Event) {
eprintln!("consumed {}", event.type_field);
eprintln!("consumed {}", event.event_type);
}

fn main() {
Expand Down
59 changes: 59 additions & 0 deletions sdk/typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Pitot TypeScript SDK

The TypeScript binding for Pitot: typed wire types plus a tiny stdio runner for
building a **Controller** (decides `control.requested` → allow/deny) or a passive
**Consumer** (reads the observed action/event stream). It speaks the same
JSON-Lines protocol as every other Pitot SDK — proven identical by the
cross-language conformance suite ([`../tests/test_runners.py`](../tests/test_runners.py)).

## Install

Pitot SDKs install from the project's own registry through the CLI front door,
pinned to the CLI's version — never public npm:

```bash
pitot install typescript # scoped .npmrc + @operatorstack/pitot@<version>
```

`pitot init --language typescript` scaffolds a complete, runnable project and
registers it as a tenant fragment; the snippets below are the core of what it
writes.

## Build a Controller

A Controller receives a typed `ControlRequested` and returns an `Outcome`. Return
`allow(...)` or `deny(...)`; the runner serializes one `control.response` per line,
in request order.

```typescript
import { runController, allow, deny, ControlRequested } from "@operatorstack/pitot";

runController("my-controller", async (req: ControlRequested) => {
if (req.kind === "shell" && String(req.data).includes("rm -rf")) {
return deny("blocked a destructive shell command");
}
return allow("approved");
});
```

## Build a Consumer

A Consumer passively reads the event stream. Keep stdout clean — a Consumer must
never write to stdout (the conformance suite enforces this); log to stderr.

```typescript
import { runConsumer, Event } from "@operatorstack/pitot";

runConsumer(async (event: Event) => {
console.error("observed", event.type);
});
```

## Run it behind an agent

```bash
pitot dev --host claude -- claude -p "…" # discovers your fragment, starts the controller
```

See the repository [README](../../../public-readme-preview/README.md) for the full
runtime, host-hook wiring, and the enforcement boundary.
Loading