{"id":51083254,"url":"https://github.com/open-gitagent/langship.sh","last_synced_at":"2026-06-23T20:02:16.473Z","repository":{"id":357157898,"uuid":"1235625452","full_name":"open-gitagent/langship.sh","owner":"open-gitagent","description":"The platform for shipping, governing AI agents — framework-agnostic, multi-runtime, GitOps-native, self-hosted.","archived":false,"fork":false,"pushed_at":"2026-05-11T14:40:09.000Z","size":361,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-11T16:36:54.064Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.langship.sh/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/open-gitagent.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-11T13:56:55.000Z","updated_at":"2026-05-11T14:49:45.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/open-gitagent/langship.sh","commit_stats":null,"previous_names":["open-gitagent/langship.sh"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/open-gitagent/langship.sh","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-gitagent%2Flangship.sh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-gitagent%2Flangship.sh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-gitagent%2Flangship.sh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-gitagent%2Flangship.sh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/open-gitagent","download_url":"https://codeload.github.com/open-gitagent/langship.sh/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-gitagent%2Flangship.sh/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34704748,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-23T02:00:07.161Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-23T20:02:15.141Z","updated_at":"2026-06-23T20:02:16.467Z","avatar_url":"https://github.com/open-gitagent.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# Langship\n\n**Any framework. Any runtime.**\n\nOpen-source, self-hosted **deployment · governance · operations** for agent apps.\nOne pipeline definition → Kubernetes, AWS Bedrock AgentCore, or Vertex AI Agent\nEngine — same governance everywhere. Works with LangGraph, LangChain, LlamaIndex,\nCrewAI, AutoGen, or raw-SDK agents. No framework lock-in.\n\n[langship.sh](https://langship.sh) · [github.com/open-gitagent/langship.sh](https://github.com/open-gitagent/langship.sh) · [CLI](./langship-cli/) · Apache 2.0\n\n\u003c/div\u003e\n\n---\n\n- [What you get](#what-you-get)\n- [5 minutes to a green run](#5-minutes-to-a-green-run)\n- [Architecture](#architecture)\n- [Repo map](#repo-map)\n- [The `langship` CLI](#the-langship-cli)\n- [Concepts](#concepts)\n- [Nodes](#nodes)\n- [Reference — env vars \u0026 make targets](#reference)\n- [Contributing \u0026 community](#contributing--community)\n- [License](#license)\n\n---\n\n## What you get\n\n| | |\n|---|---|\n| **Pipelines as graphs** | Drag-and-drop CI/CD nodes — Trigger → Build → Scan/SAST → Eval → Policy → Approval → Deploy → Promote → Rollback. n8n-shaped JSON on disk; YAML in git is the source of truth. |\n| **Governance is a node** | Approvals, policy checks, eval gates, PII/secret scans are first-class, reorderable steps in the graph — not middleware you can't see. |\n| **Any runtime, one pipeline** | Same definition deploys to K8s, Bedrock AgentCore, or Vertex Agent Engine. (Today the Deploy node ships to **Bedrock AgentCore** end-to-end; K8s / Vertex are stubbed.) |\n| **Durable by construction** | Restate journals every node (`restate.Run(\"node:\u003cname\u003e\", fn)`) — crash-safe replay, awakeable-based human approvals (timeout → auto-reject). |\n| **GitOps promotion** | A Promote node opens/merges a PR `fromBranch → toBranch` on the agent's repo; the merge fires the next environment's pipeline. Promotion is an auditable event. |\n| **Real OCI builds** | BuildKit solves your Dockerfile against the cloned repo, pushes to GHCR or any registry (private-repo PAT support). Mirror to N registries with the Push node. |\n| **Operate, don't just deploy** | Live SSE log streams + canvas-overlay status rings; per-node logs archived to S3-compatible storage. |\n| **Self-hosted, end-to-end** | Your cloud credentials, agent code, and run history never leave your network. Secrets AES-GCM sealed at rest. |\n| **CLI-first** | `langship` — agents, envs, pipelines, creds, runs from your terminal. `git push` to ship. |\n\n---\n\n## 5 minutes to a green run\n\n**0. Start everything.** Base compose bundles every service `flow` needs —\n`mongo, restate, buildkitd, registry, minio, flow, web`.\n\n```sh\ndocker compose up\n```\n\n| | URL |\n|---|---|\n| UI | http://localhost:3000 |\n| API | http://localhost:8090 |\n| Restate | `:8081` ingress · `:9070` admin |\n| BuildKit | `tcp://127.0.0.1:1234` |\n| Registry | `127.0.0.1:5050` (host port; buildkitd pushes to `registry:5000` internally) |\n| MinIO | `127.0.0.1:9000` (S3 API) · `:9001` console (`minio` / `minio12345`) |\n\n\u003e If a sibling stack already owns one of those host ports, stop it or override the\n\u003e mapping in a `compose.override.yml`.\n\n**1. Install the CLI and point it at the API.**\n\n```sh\npip install -e ./langship-cli          # optional: pip install pyyaml  (for -o yaml)\nlangship login --api-url http://localhost:8090\n```\n\n**2. Register an agent (a git repo) and push a pipeline.**\n\n```sh\nlangship agents create --repo https://github.com/you/your-agent --pat ghp_...\nlangship pipelines push examples/hello.json        # prints the new pipeline id\n```\n\n**3. Wire it into an environment, follow it, run it.**\n\n```sh\nlangship envs create dev -d \"Auto-deploy on push\"\nlangship envs add-pipeline dev \u003cpipelineId\u003e\nlangship agents follow-env \u003cagentId\u003e dev\nlangship agents trigger \u003cagentId\u003e                  # → prints execution id(s)\n```\n\n**4. Watch it run.**\n\n```sh\nlangship runs logs \u003cexecutionId\u003e -f                # live SSE stream\n# or open the UI: http://localhost:3000/executions/view?id=\u003cexecutionId\u003e\n```\n\nThat's the loop: `agent → env → pipeline → trigger → durable run → status`.\n\n### Hot-reload dev (three terminals)\n\n```sh\n# 1) backing services only\ndocker compose up -d mongo restate            # + buildkitd/registry from the overlay\n\n# 2) Go API with air — rebuilds on .go change\nmake watch                                    # or `make serve` for a stable binary\n\n# 3) Next dev server with HMR; /api proxies to :8090\nmake dev\n```\n\n`make watch` pre-exports env defaults matching the compose host ports — override\nany at the CLI, e.g. `make watch MINIO_ENDPOINT=...`. Set `FLOW_SECRET_KEY` in\nyour shell before touching anything credential/environment-related (the API\nrefuses credential writes without it).\n\n---\n\n## Architecture\n\nThree layers, all run by you:\n\n```\n            ┌──────────────────────────────────────────────────────────┐\n  CLI ──────►  API / control plane   (Go — pkg/api)                     │\n  UI  ──────►   REST + SSE · agents/envs/pipelines/creds/runs · webhooks │\n            └─────────────┬────────────────────────────────────────────┘\n                          │ RunAsync\n            ┌─────────────▼────────────────────────────────────────────┐\n            │  Orchestration   (Restate cluster + worker)              │\n            │  DAG walk (pkg/orchestrator) → executors (pkg/executors) │\n            │  every node = restate.Run(\"node:\u003cname\u003e\", fn)            │\n            └─────────────┬────────────────────────────────────────────┘\n                          │\n            ┌─────────────▼────────────────────────────────────────────┐\n            │  Data:  MongoDB  (pipelines · runs · agents · creds ·    │\n            │                   environments)                          │\n            │         MinIO / S3  (archived per-node logs, artifacts)  │\n            │         Postgres  — Restate's backing store ONLY         │\n            │         pkg/secrets  — AES-GCM seal/open (FLOW_SECRET_KEY)│\n            └──────────────────────────────────────────────────────────┘\n                          ▲\n            GitHub webhook │  /webhooks/github/{id}  (HMAC-verified)\n                           │  push → branch filter → dispatch run(s)\n```\n\nA run's lifecycle: webhook (or `langship agents trigger`) → the dispatcher walks\nthe agent's followed environments, applies each pipeline's branch filter, stamps\n`agentId / environment / fromBranch` into the trigger payload, and calls\n`orchestrator.RunAsync` → the DAG walker runs nodes in topological order, each\nwrapped in `restate.Run` → terminal status written back to Mongo `runs` → SSE\nclients (`/api/executions/{id}/stream`) get `node_started / node_log /\nnode_completed / node_error / done` events live.\n\n\u003e **Why these choices** — Restate gives crash-safe journaling + awakeables (human\n\u003e approval that survives a restart) for free; Mongo is the app store; Postgres is\n\u003e *only* Restate's persistence and is never touched by app code; BuildKit does\n\u003e real OCI builds without a Docker daemon. See [aude.md](./aude.md) for the full\n\u003e rationale.\n\n---\n\n## Repo map\n\n```\ncmd/flow/            the `flow` server binary (API + Restate worker entry point)\npkg/\n  api/               REST + SSE handlers (agents, envs, pipelines, creds, runs, webhooks)\n  orchestrator/      DAG walk; Approval is special-cased out of restate.Run (it\n                     calls restate.Set/Clear directly)\n  engine/            execution context, ExecutionEvent, the executor lookup\n  executors/         node implementations + the registry:\n                       trigger · build · push · sast · imagescan · approval ·\n                       promote · deploy · (test/eval/policy/rollback stubs)\n  awsdeploy/         AWS Bedrock AgentCore adapter — STS AssumeRole, idempotent\n                     ECR + IAM bootstrap, control-plane SigV4, endpoint wait\n  github/            REST helpers — webhook install/verify, PRs, merges\n  storage/           Mongo-backed stores: pipelines, runs, agents, credentials,\n                     environments\n  secrets/           AES-GCM SealString/OpenString keyed off FLOW_SECRET_KEY\nweb/                 Next.js 15 UI (static export) — canvas, runs, agents,\n                     environments, credentials\nlangship-cli/        the `langship` Python CLI (Typer / Rich / httpx)\nexamples/            sample pipeline JSON\n```\n\n---\n\n## The `langship` CLI\n\nThe daily driver for agent devs; the bootstrap surface for platform engineers.\n\n```sh\npip install -e ./langship-cli                # + pip install pyyaml  for -o yaml\nlangship login --api-url http://localhost:8090   # saved to ~/.langship/config.toml\n\n# the loop\nlangship agents create --repo https://github.com/you/agent --pat ghp_...\nlangship pipelines push prod.yaml --id \u003cpipelineId\u003e     # create-or-update from a file\nlangship envs create prod -d \"Strict gates\"\nlangship envs add-pipeline prod \u003cpipelineId\u003e\nlangship envs reorder prod \u003cpid1\u003e \u003cpid2\u003e \u003cpid3\u003e          # promotion order\nlangship agents follow-env \u003cagentId\u003e prod\nlangship agents trigger \u003cagentId\u003e\nlangship runs logs \u003cexecutionId\u003e -f\n\n# credentials (server needs FLOW_SECRET_KEY)\nlangship creds create prod-aws --type aws \\\n  --aws-region us-east-1 --aws-account 123456789012 \\\n  --aws-role-arn arn:aws:iam::123456789012:role/FlowDeployRole\n```\n\nCommand groups: `agents`, `envs`, `pipelines`, `creds`, `runs` — each with\n`--help`. `-o json` / `-o yaml` on list/get commands. `LANGSHIP_API_URL` /\n`LANGSHIP_TOKEN` override the saved config. Full reference:\n[`langship-cli/README.md`](./langship-cli/README.md).\n\n---\n\n## Concepts\n\n- **Agent** — a registered git repo (URL + PAT). One-click GitHub webhook\n  install; `/webhooks/github/{id}` verifies the HMAC signature and dispatches runs\n  on push. An agent **follows environments** (`agent.environments[]`); triggering\n  it runs the pipelines of every followed env. Agents may carry per-agent\n  credential overrides.\n- **Environment** — a named, **ordered list of pipelines** (the promotion\n  sequence; reorderable). Global. Purely a sequencing container — per-deploy\n  config lives on the nodes, not the env. `dev` / `staging` / `prod` / custom.\n- **Pipeline** — a DAG of nodes built on the canvas (n8n-shape JSON underneath),\n  stored in Mongo, loaded fresh per run. The Trigger node carries `fromBranch` /\n  `toBranch`; a per-pipeline branch filter decides which pipelines run for a given\n  push.\n- **Credential** — a named record (`aws` / `gcp` / `kv`) in a global pool, with\n  optional per-agent overrides. Secret fields are AES-GCM sealed at rest with\n  `FLOW_SECRET_KEY`. Deploy / Push look one up by name.\n- **Run** — one execution of a pipeline. Restate journals each node. Terminal\n  status is written back to Mongo's `runs` collection. The dispatcher stamps\n  `agentId`, `environment`, and `fromBranch` into the trigger payload; each node\n  emits a `__\u003cnode\u003e` summary object on its output items.\n- **Live view** — `/executions/view?id=…` subscribes to\n  `/api/executions/{id}/stream` (SSE) for `node_started`, `node_completed`,\n  `node_error`, **`node_log`**, and `done` events; the canvas overlays status\n  rings on each node.\n\n---\n\n## Nodes\n\n| Node | What it does |\n|---|---|\n| **Trigger** | Entry point; carries `fromBranch` / `toBranch` for the branch filter + Promote. |\n| **Build** | Clones the agent repo (`fromBranch`), builds an OCI image via BuildKit (`mode: docker`) or runs `/bin/sh -c \u003ccommand\u003e` in the clone (`mode: shell`). GHCR auth uses the agent's PAT (`write:packages`); `localhost:*` / `registry:*` are anonymous + insecure. Streams BuildKit's plain-mode progress as `node_log` events. |\n| **Push** | Mirrors the built image to one or more registries (go-containerregistry's `crane`). |\n| **SAST / ImageScan** | Sibling-container scanners — trivy / semgrep / gitleaks / SonarCloud / grype — over the source / image. Configurable severity threshold and fail-on-finding. |\n| **Approval** | Pauses on a Restate awakeable until resumed via `POST /api/executions/{id}/resume` (UI or `langship`). `method: ui \\| quorum \\| auto`; optional `timeoutSeconds` → auto-reject. Two outputs: approved (0) / rejected (1). |\n| **Promote** | Opens or merges a PR `fromBranch → toBranch` on the agent's repo via the GitHub API — idempotent (re-finds an existing PR). Modes: `open-pr` / `merge` / `merge-pr`. Emits `__promote` with the PR number / URL. The merge fires the next env's pipeline. |\n| **Deploy** | Deploys the upstream Push image to **AWS Bedrock AgentCore** (`target: agentcore`; k8s / vertex are stubs). Looks up an `aws` credential by name, assumes the cross-account role, idempotently provisions the ECR repo + the shared `agentcore-runtime-role` IAM role, creates/updates the runtime, waits for the endpoint to be `READY`, and emits `__deploy` with the public invoke URL. |\n| **Test / Eval / Policy / Rollback** | Stubbed for now — visible on the canvas, no-op executors. |\n\nAdding a node? See the \"Adding a node executor\" section in\n[CONTRIBUTING.md](./CONTRIBUTING.md).\n\n---\n\n## Reference\n\n### Env vars (the `flow` process — `./bin/flow serve`, `make watch`, or compose)\n\n| Var | Default | Notes |\n|---|---|---|\n| `FLOW_ADDR` | `:8090` | API listen address |\n| `FLOW_CORS_ORIGINS` | `*` (compose: `http://localhost:3000`) | CSV allowlist |\n| `FLOW_PUBLIC_URL` | (empty) | Externally-reachable base URL for webhook callback URLs. Set to your `cloudflared` tunnel for GitHub webhooks. |\n| `FLOW_SECRET_KEY` | (unset → credential writes refused) | Master key for AES-GCM sealing of credentials/secrets. Any string; hashed to 32 bytes. **Losing it makes sealed data unrecoverable.** |\n| `MONGO_URI` | (required; compose: `mongodb://localhost:27017`) | |\n| `MONGO_DB` | `flow` | |\n| `RESTATE_INGRESS_URL` | `http://localhost:8081` | |\n| `RESTATE_ADMIN_URL` | `http://localhost:9070` | |\n| `RESTATE_SERVICE_ADDR` | `:9080` | Service-endpoint listen addr |\n| `RESTATE_DEPLOYMENT_URI` | `http://host.docker.internal:9080` | How Restate reaches us; compose overrides to `http://flow:9080` |\n| `BUILDKIT_HOST` | `tcp://127.0.0.1:1234` | BuildKit gRPC; compose: `tcp://buildkitd:1234` |\n| `MINIO_ENDPOINT` / `MINIO_ACCESS_KEY` / `MINIO_SECRET_KEY` / `MINIO_BUCKET` / `MINIO_USE_SSL` | `127.0.0.1:9000` / `minio` / `minio12345` / `flow-logs` / `false` | Archived per-node log storage |\n\nCLI env: `LANGSHIP_API_URL`, `LANGSHIP_TOKEN` (override `~/.langship/config.toml`).\n\n### Make targets\n\n| | |\n|---|---|\n| `make build` | build the web bundle then the Go binary (`bin/flow`) |\n| `make build-go` | Go binary only (expects `web/dist` to exist) |\n| `make serve` | `build-go` then `./bin/flow serve` — stable binary |\n| `make watch` | Go API with `air` (rebuilds on `.go` change), env defaults pre-exported |\n| `make dev` | Next dev server with HMR (`/api` proxies to `:8090`) |\n| `make web` | build the Next static export |\n| `make test` / `make vet` / `make tidy` | `go test ./...` / `go vet ./...` / `go mod tidy` |\n\n---\n\n## Contributing \u0026 community\n\n- **Issues \u0026 discussion** — [github.com/open-gitagent/langship.sh/issues](https://github.com/open-gitagent/langship.sh/issues) for bugs and feature requests. Search first.\n- **Contributing** — [CONTRIBUTING.md](./CONTRIBUTING.md): dev setup, what to run before a PR, conventions, how to add a node executor. Contributions accepted under Apache 2.0.\n- **Code of conduct** — [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) (Contributor Covenant). Report concerns to \u003ckhush@lyzr.ai\u003e.\n- **Security** — **do not** file public issues for vulnerabilities. See [SECURITY.md](./SECURITY.md) — report privately to \u003ckhush@lyzr.ai\u003e.\n\n## License\n\n[Apache 2.0](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopen-gitagent%2Flangship.sh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopen-gitagent%2Flangship.sh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopen-gitagent%2Flangship.sh/lists"}