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
79 changes: 79 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# GoReleaser builds the prebuilt Pitot binaries for each release tag. Pitot is
# pure Go / CGO-free, so cross-compiling the five supported targets is a plain
# matrix build. The release workflow (works-rendered release.yml installed in
# the public repo) tags a version, runs `goreleaser release --clean`, attests
# provenance, then uploads the archives + checksums.txt to the GCP Artifact
# Registry generic `dist` repo fronted by get.operatorstack.systems — which is
# exactly where the repo shim (.pitot/bin/pitot) and `pitot upgrade` hydrate
# from.
#
# Local check: `goreleaser check` (config only) or `goreleaser release
# --snapshot --clean` (full dry run into ./dist, no publish).
version: 2

project_name: pitot

builds:
- id: pitot
main: ./cmd/pitot
binary: pitot
env:
- CGO_ENABLED=0
flags:
- -trimpath
# Version identity is stamped into package main (see cmd/pitot/version.go),
# matching GoReleaser's default variable names.
ldflags:
- -s -w -X main.version={{ .Version }} -X main.commit={{ .ShortCommit }} -X main.date={{ .CommitDate }}
mod_timestamp: "{{ .CommitTimestamp }}"
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
# Five supported targets: darwin arm64/amd64, linux amd64/arm64, windows amd64.
ignore:
- goos: windows
goarch: arm64

archives:
- id: pitot
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
formats:
- tar.gz
format_overrides:
- goos: windows
formats:
- zip
# The public repo carries README.md at the root (projected from
# public-readme-preview). No LICENSE file exists yet — add it here when one
# lands.
files:
- README.md

checksum:
name_template: checksums.txt
algorithm: sha256

# One CycloneDX SBOM per archive, generated by syft (installed by the workflow).
sboms:
- id: archive
artifacts: archive

# Binaries are published to GCP Artifact Registry (the generic `dist` repo) by
# the release workflow (gcloud artifacts generic upload), fronted by
# get.operatorstack.systems. GoReleaser only builds + checksums + SBOMs; it
# does NOT create a GitHub Release.
release:
disable: true

changelog:
use: github
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "^chore:"
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,47 @@ single-response rule before carrying the answer back. Pitot does not know what

## Install

Download a release binary for macOS, Linux, or Windows, or install from source:
**Nobody installs Pitot per machine — the repository pins it.** `pitot init`
writes two substrate files alongside your tenant fragments:

- `.pitot/version` — one committed semver line, the only version authority
- `.pitot/bin/pitot` (+ `pitot.ps1`) — a committed shim that reads the pin,
hydrates exactly that release into a per-user cache
(`~/.cache/pitot/<version>/`, sha256-verified against the published
`checksums.txt`), and execs it

Fresh clones, CI, and cloud agents run `.pitot/bin/pitot` with zero setup —
the first invocation hydrates, every later one is a cache hit. There is no
fallback to whatever binary happens to be on PATH; a missing release with no
network fails closed with a named error, and `PITOT_NO_HYDRATE=1` makes
hydration cache-only.

**Upgrades are a reviewed diff.** `pitot upgrade` verifies the new release,
re-checks every tenant fragment against it, and rewrites the one pin line —
nothing else. Commit that diff and every clone hydrates the new version on
its next invocation; roll back by reverting it.

```bash
pitot upgrade --check # report pinned vs latest
pitot upgrade # hydrate, validate tenants, rewrite .pitot/version
```

For a global CLI convenience (running `pitot init` in new repos), grab a
release binary or build from source:

```bash
go install github.com/operatorstack/pitot/cmd/pitot@latest
```

Inspect the effective local boundary at any time:
Typed SDKs install from our own registry through the distribution front door
— never public npm/PyPI, pinned to the CLI's version:

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

Inspect the effective local boundary, pin, and cache state at any time:

```bash
pitot doctor
Expand Down
17 changes: 13 additions & 4 deletions UPSTREAM.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"files": {
".goreleaser.yml": "d4ba852e8c1b8801a562c048b7200ffd9ff74386f72f125f297096846c233bc5",
"CONTRIBUTING.md": "23728d8a132d62b8adfb2e5c3eb9d9bfcf8a4d04543765b1e22ad8d55424af8f",
"README.md": "e4a7587107f7db1eb75859f62f6abfb780c99e2b210c808dda3aaa25612eb172",
"README.md": "74c442eb9d40e3c50e2781166798244698c929dc8b4d7a0257f269040a697233",
"adapter-verification.json": "f8ad4e206571650f698826a8b66d8c00822be425e8d2de8ae98d98239e575eb4",
"adapters/adapters.go": "1b46ba131fa3b2c93eed23526330275a3506451ba4bbd4f497e5378dfab2b6a8",
"assets/pitot-boundary.png": "8a0ddb7d81831d94e14813f50ea4ca8670d77417f339ed2f91f0c653bf52f41d",
Expand All @@ -16,11 +17,16 @@
"cmd/generate-schema/main.go": "6e9d0030290d99e36967433f96e38385a122974f899ad9421aac1ef7e50d8fcb",
"cmd/pitot/doctor_host.go": "7ecade40618bfb3510ae8e55fa802361371b4f7fbafedcd61233d19ef46cb219",
"cmd/pitot/doctor_host_test.go": "4e6e327f6cf27cf94a0a608e10eb6790d6c11fcd53e6dfd7370007190749952f",
"cmd/pitot/install.go": "016829971a80544bded2231bc9d08f7d2d9abb2be57299b88b035005061d53d3",
"cmd/pitot/kimi_control_test.go": "27b38867d4799636a664e3b1726ef55568f5dfd6a9be11f0ce5eca9931d759a4",
"cmd/pitot/kimi_smoke_test.go": "01cbf18312902cac42ec1f2547d35362c2bc920938acea508ba7c7f0638a9473",
"cmd/pitot/main.go": "9baae94a571c20b7c2d4e11985026ebaa8fe256372ce70c3198a73cc82d8390b",
"cmd/pitot/main.go": "69d1571a56ae1c024fcd697bb406c3aac0f4774d62b41f3444a949ffe6f4c4d4",
"cmd/pitot/main_test.go": "b381fe30dafe3299c82fe23e1899b64f79c4a2b27059b78823179625e3b6679e",
"cmd/pitot/workbench.go": "976c81951c565da1a897637566f662c493b2fe6d279debe852dc783b6fac1db4",
"cmd/pitot/shim.go": "a2a605b3126d4be7d3e2a587f09259a5f6e808c3a132480a760661cf34c1796f",
"cmd/pitot/substrate_test.go": "016c2fec0d61ffb725efa24fc177c6baaa71c69163fc88dbd914cc615275c03b",
"cmd/pitot/upgrade.go": "3f7adf156b04f9bac4847ed66c5fa69dc0906f482cb1362f60cbf3416a5733fc",
"cmd/pitot/version.go": "60ddbabcc8985c5137d5b73e3ba1be0f62fbb9db2a6612027e08314dfd82d618",
"cmd/pitot/workbench.go": "afbba5557974c1a4fb28ed2097ec8bea4d9bb1fa97cda931c9ebf52daec24915",
"cmd/pitot/workbench_build_test.go": "8d5c5c35e8cbd59e21cfdd7e206d6b5b769892ab27a99fe5d87b13b1a31d0714",
"cmd/pitot/workbench_contract_test.go": "5e465f3d3f8b93ffaacfb4738279d369b13514bf7613b8611952f3cd26896586",
"cmd/pitot/workbench_dev_test.go": "abffe81e1a25f086d7f3c2f1c32986ee93618bbfc87220bdfda840ec6c6466bf",
Expand All @@ -40,6 +46,9 @@
"examples/token-meter/main.go": "4b1b9c1a43c3cf48b09dba6f607776caced9d2b5b562373496b31ed184582dd1",
"go.mod": "5b5f818251bbfb9dc3c066b70c3c33512ed18486d5713ba60e7f3141e23139fe",
"go.sum": "6e1aefcb6cb7d180f4b9f3eec64b547945cbebf660764b2bc3818e69bb7c8827",
"hydrate/doc.go": "bd350bf37c6357a1d2880ec330f3a5680fd71be682c1f6bbfa5f0889fe07acf5",
"hydrate/hydrate.go": "2d406af04c18c4504c39b4792d396d37cbfead12051f29ebf78f737a52f21728",
"hydrate/hydrate_test.go": "a055ef69c8560db75a47b1d1a8b6c703f6caccfa967199140a37ac8894a9522a",
"integrations/codex/PreToolUse.ps1": "a032bdefcaa6bdabc240ea5a91b19caa9804e976697ae67cbbb9c3a407fa4450",
"integrations/copilot/PreToolUse": "1a487ca7dfaaa21cbc7db5cec0091fed8ce9337bc116b9a6dec0a109ac5802a5",
"integrations/copilot/PreToolUse.ps1": "da5d113b9fd9441e8646414c3a1eb4754b415f40db7d06cbce111b123cdb2c88",
Expand Down Expand Up @@ -121,7 +130,7 @@
"generator": "operatorstack/pitot:project",
"schema_version": 1,
"source": {
"commit": "4cd27ab7a10e85184157f71249708254fbf7c208",
"commit": "107c94141e23b7df3449cfa6e7b12a6d45052563",
"path": "labs/15-pitot",
"repository": "operatorstack/intelligence-flow"
}
Expand Down
173 changes: 173 additions & 0 deletions cmd/pitot/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// Install wires a project to the typed Pitot SDKs from our own registry,
// through the distribution front door — never public npm/PyPI
// (packages-come-from-our-registry-or-nowhere). Bindings are pinned to the
// CLI's own release version (bindings-move-in-lockstep-with-the-CLI).
package main

import (
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"

"github.com/operatorstack/pitot/hydrate"
)

const (
npmScope = "@operatorstack"
npmClientPkg = "@operatorstack/pitot"
pyClientPkg = "operatorstack-pitot"
pyRegistryRef = ".pitot/registry"
)

func runInstall(args []string, stdout, stderr io.Writer) error {
if len(args) == 0 {
return errors.New("pitot install: requires a language (typescript, python)")
}
lang := args[0]
configureOnly := false
revert := false
for i := 1; i < len(args); i++ {
switch args[i] {
case "--configure-only":
configureOnly = true
case "--revert":
revert = true
case "--host":
if i+1 >= len(args) {
return errors.New("pitot install: --host requires a value")
}
os.Setenv(hydrate.EnvHost, args[i+1])
i++
default:
return fmt.Errorf("pitot install: unexpected argument %q", args[i])
}
}

switch lang {
case "typescript":
if revert {
return revertNPM(stdout)
}
return installNPM(stdout, configureOnly)
case "python":
if revert {
return revertPython(stdout)
}
return installPython(stdout, configureOnly)
default:
return fmt.Errorf("pitot install: unsupported language %q (want typescript, python)", lang)
}
}

// npmRegistryLine is the single scoped line install owns in .npmrc: only the
// @operatorstack scope resolves through the front door; everything else stays
// on public registries.
func npmRegistryLine() string {
return npmScope + ":registry=" + hydrate.BaseURL() + "/npm/"
}

func installNPM(stdout io.Writer, configureOnly bool) error {
if err := upsertNPMRC(npmRegistryLine()); err != nil {
return err
}
fmt.Fprintf(stdout, "configured .npmrc: %s\n", npmRegistryLine())
spec := npmClientPkg + versionSpec("@")
if configureOnly {
fmt.Fprintf(stdout, "next: npm install %s\n", spec)
return nil
}
return runTool(stdout, "npm", "install", spec)
}

func installPython(stdout io.Writer, configureOnly bool) error {
index := hydrate.BaseURL() + "/pip/simple/"
if err := os.MkdirAll(filepath.Dir(pyRegistryRef), 0o755); err != nil {
return err
}
if err := os.WriteFile(pyRegistryRef, []byte("PIP_INDEX_URL="+index+"\n"), 0o644); err != nil {
return err
}
fmt.Fprintf(stdout, "configured %s: PIP_INDEX_URL=%s\n", pyRegistryRef, index)
spec := pyClientPkg + versionSpec("==")
if configureOnly {
fmt.Fprintf(stdout, "next: pip install --index-url %s %s\n", index, spec)
return nil
}
if _, err := exec.LookPath("uv"); err == nil {
return runTool(stdout, "uv", "pip", "install", "--index-url", index, spec)
}
return runTool(stdout, "pip", "install", "--index-url", index, spec)
}

// versionSpec pins the binding to the CLI's own release version; a dev build
// cannot vouch for a binding version and installs unpinned with a warning.
func versionSpec(separator string) string {
if v := releaseVersion(); v != "dev" {
return separator + v
}
return ""
}

func upsertNPMRC(line string) error {
existing, err := os.ReadFile(".npmrc")
if err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
var kept []string
for _, l := range strings.Split(string(existing), "\n") {
if l == "" || strings.HasPrefix(l, npmScope+":registry=") {
continue
}
kept = append(kept, l)
}
kept = append(kept, line)
return os.WriteFile(".npmrc", []byte(strings.Join(kept, "\n")+"\n"), 0o644)
}

func revertNPM(stdout io.Writer) error {
existing, err := os.ReadFile(".npmrc")
if errors.Is(err, os.ErrNotExist) {
return nil
}
if err != nil {
return err
}
var kept []string
for _, l := range strings.Split(strings.TrimRight(string(existing), "\n"), "\n") {
if strings.HasPrefix(l, npmScope+":registry=") {
continue
}
kept = append(kept, l)
}
if len(kept) == 0 {
fmt.Fprintln(stdout, "removed .npmrc")
return os.Remove(".npmrc")
}
fmt.Fprintln(stdout, "removed the "+npmScope+" registry line from .npmrc")
return os.WriteFile(".npmrc", []byte(strings.Join(kept, "\n")+"\n"), 0o644)
}

func revertPython(stdout io.Writer) error {
if err := os.Remove(pyRegistryRef); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
fmt.Fprintln(stdout, "removed "+pyRegistryRef)
return nil
}

func runTool(stdout io.Writer, name string, args ...string) error {
command := exec.Command(name, args...)
command.Stdout = stdout
command.Stderr = stdout
if releaseVersion() == "dev" {
fmt.Fprintln(stdout, "warning: dev build — installing the binding unpinned; a released pitot pins bindings to its own version")
}
if err := command.Run(); err != nil {
return fmt.Errorf("pitot install: %s %s: %w", name, strings.Join(args, " "), err)
}
return nil
}
16 changes: 16 additions & 0 deletions cmd/pitot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ func runWithIO(ctx context.Context, args []string, stdin io.Reader, stdout, stde
return runHook(ctx, args[1:], stdin, stdout, stderr)
case "request":
return runRequest(ctx, args[1:], stdout)
case "version", "--version", "-v":
return runVersion(stdout)
case "upgrade":
return runUpgrade(ctx, args[1:], stdout, stderr)
case "install":
return runInstall(args[1:], stdout, stderr)
case "-h", "--help", "help":
fmt.Fprint(stdout, usage())
return nil
Expand Down Expand Up @@ -215,9 +221,11 @@ func doctor(args []string, stdout, stderr io.Writer) error {
}

fmt.Fprintf(stdout, "Pitot %s — local boundary\n", schema.Version)
fmt.Fprintf(stdout, "binary version: %s (%s)\n", releaseVersion(), buildCommit())
fmt.Fprintf(stdout, "adapter version: %s\n", adapters.AdapterVersion)
fmt.Fprintln(stdout, "unauthenticated local socket: none")
fmt.Fprintln(stdout, "runtime capabilities: hook_control consumer_delivery explicit_request")
printHydrationStatus(stdout)
fmt.Fprintln(stdout, "hosts:")
for _, host := range adapters.Supported() {
probe, err := adapters.CanonicalHookEvent(host)
Expand Down Expand Up @@ -312,10 +320,18 @@ usage:
pitot run [--config PATH] --runtime PATH
pitot hook HOST [--runtime PATH]
pitot request KIND [--data JSON] --runtime PATH
pitot version
pitot upgrade [--to X.Y.Z] [--check] [--host HOST]
pitot install typescript|python [--configure-only] [--revert] [--host HOST]

configuration is tenant-partitioned: each tool or user registers its processes
in its own fragment under .pitot/conf.d/; the runtime merges every fragment and
rejects collisions. --config PATH overrides discovery with one explicit file.

the binary is repo-pinned: .pitot/version names the exact release and the
committed shim .pitot/bin/pitot hydrates it on demand (sha256-verified, cached
per user). upgrade rewrites the pin only — commit that diff to upgrade every
clone. install wires typed SDKs from our registry through the front door.
`
}

Expand Down
Loading
Loading