{"id":50945120,"url":"https://github.com/nonrational/adjacent","last_synced_at":"2026-06-17T19:03:15.892Z","repository":{"id":251399305,"uuid":"837303533","full_name":"nonrational/adjacent","owner":"nonrational","description":"Agents and humans live adjacently. ","archived":false,"fork":false,"pushed_at":"2026-06-14T23:28:06.000Z","size":233,"stargazers_count":2,"open_issues_count":11,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-15T01:14:26.272Z","etag":null,"topics":["agentic","dev-server","dns","rust"],"latest_commit_sha":null,"homepage":"https://adj.ac/ent","language":"Rust","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/nonrational.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE-APACHE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/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":"2024-08-02T16:48:34.000Z","updated_at":"2026-06-14T22:29:58.000Z","dependencies_parsed_at":"2024-08-02T18:49:49.087Z","dependency_job_id":null,"html_url":"https://github.com/nonrational/adjacent","commit_stats":null,"previous_names":["nonrational/adj.ac-ent","nonrational/adjacent"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/nonrational/adjacent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nonrational%2Fadjacent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nonrational%2Fadjacent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nonrational%2Fadjacent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nonrational%2Fadjacent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nonrational","download_url":"https://codeload.github.com/nonrational/adjacent/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nonrational%2Fadjacent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34461618,"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-17T02:00:05.408Z","response_time":127,"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":["agentic","dev-server","dns","rust"],"created_at":"2026-06-17T19:03:15.058Z","updated_at":"2026-06-17T19:03:15.887Z","avatar_url":"https://github.com/nonrational.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Adjacent\n\nA local dev-server harness so a human developer and an agent developer can share one supervised server instance instead of fighting for control of the process.\n\nWhen both sides need the same local server running, they evict each other. The agent takes over → the developer loses log visibility. The developer reclaims it → the agent can't validate its work. Adjacent owns the process so neither side has to.\n\nHomepage: [adj.ac/ent](https://adj.ac/ent)\n\n## Agentic by Design\n\n- **One CLI for humans \u0026 agents.** One surface, `adj`, with `--json` on every read command ([schema](crates/adj/JSON.md)). No separate agent mode — parity keeps the contract simple and stops the two sides drifting apart.\n\n- **Config lives in code.** `adjacent.toml` sits in the app directory and registers via `adj add \u003cpath\u003e`. The boot command and idle timeout are part of the repo, not buried in a global registry. Agents make config maintenance cheap.\n\n- **A URL per worktree.** Several agents in parallel git worktrees of one repo can all register. `adj add` names each instance after its branch — `feature-x.site.adj.ac` — each with its own process, port and logs. See [Worktrees](#worktrees).\n\n- **Boots on demand, stops when idle.** Apps don't run until a request hits `\u003cname\u003e.adj.ac`, and concurrent requests during boot wait on the same start. They stop after `idle_timeout` (default `\"15m\"`, accepts `\"30s\"` / `\"1h\"` / `\"off\"`) with no proxied traffic, so long sessions don't accumulate orphaned servers.\n\n- **Readiness before forwarding.** Default is TCP-connect; opt into HTTP probing with `health_check_url = \"/healthz\"`. The proxy never forwards to a half-booted process. Agents can block on this explicitly with `adj wait-ready`.\n\n- **The daemon owns ports.** `$PORT` is injected into the boot command; the app binds where it's told. Apps that need a different variable name set `port_env = \"BIND_PORT\"`.\n\n- **Logs are JSONL on disk.** `~/.adjacent/logs/\u003cname\u003e.log` is the source of truth. `adj logs` projects them for humans; `adj logs --json` streams them as-is.\n\n- **DNS is real, not `/etc/hosts`.** `*.adj.ac` resolves to `127.0.0.1` via a public wildcard A record. No hosts editing, no local resolver, nothing to install for DNS to work.\n\n- **TLS without a private key on disk.** `adj install-ca` provisions an ECDSA key in the macOS login keychain marked non-extractable; the cert is name-constrained to `*.adj.ac` so the CA cannot mint trusted certs for other domains.\n\n- **Never runs as root.** Privileged ops — pf port-forward, CA trust — emit reviewable commands the user runs with `sudo`.\n\n## Status\n\nIn development. See [github.com/nonrational/adjacent/issues](https://github.com/nonrational/adjacent/issues).\n\n## Install (alpha)\n\nApple Silicon, unsigned alpha build, straight from this repo (no separate tap repo):\n\n```sh\nbrew tap nonrational/adjacent https://github.com/nonrational/adjacent\nbrew install adj\n```\n\nThen `adj daemon` (or install it as a login service via a launchd LaunchAgent) and `adj add .` in an\napp directory. HTTPS is opt-in via `adj install-ca`; because each `brew upgrade adj` replaces the\nbinary, repair the CA afterward with `adj install-ca --reset \u0026\u0026 adj install-ca`. Building from source\ninstead? See [Local Development](#local-development).\n\n## Usage\n\nRun `adj \u003ccommand\u003e --help` for flags. Every read command supports `--json`.\n\n```zsh\nUsage: adj \u003cCOMMAND\u003e\n\nCommands:\n  daemon                Run the Adjacent daemon in the foreground\n  add                   Register an app from a directory containing adjacent.toml\n  list                  List registered apps and their state\n  up                    Boot a registered app\n  down                  Stop a running app (SIGTERM, then SIGKILL after a grace period)\n  restart               Restart an app (down then up)\n  remove                Remove an app from the registry (stopping it first if running)\n  prune                 Remove every registry entry whose directory no longer exists on disk\n  status                Report the current state of an app\n  logs                  Print the log file for an app\n  wait-ready            Block until an app reports ready (TCP-open or 2xx from health_check_url)\n  agent-instructions    Print a markdown steering doc telling AI coding agents how to interact with the Adjacent-supervised app in the target directory\n  install-port-forward  Print the pf anchor and the sudo command to redirect :80 to the proxy port\n  install-ca            Generate the local HTTPS CA (if missing) and print the sudo command to trust it\n  doctor                Verify the local install end-to-end: pf port-forward rule, daemon reachability, and the local CA (on-disk cert, keychain key, signing ACL, system trust). All checks are rootless. Exit status is 0 when everything passes, 2 when any check fails\n  help                  Print this message or the help of the given subcommand(s)\n\nOptions:\n  -h, --help     Print help\n  -V, --version  Print version\n```\n\n## Worktrees\n\nFour agents in four git worktrees of the same repo can all register. `adj add` inside a\nlinked worktree names the instance after its branch: the worktree of `site` on branch\n`feature-x` serves at `feature-x.site.adj.ac`, while the main checkout keeps `site.adj.ac`.\nNo flags needed (`--label` overrides the branch name); each worktree gets its own process,\nport and logs. When a worktree is deleted, `adj list` flags leftover entries as stale and\n`adj prune` clears them.\n\n## Agent Integration\n\nWhen an agent runs in a directory with `adjacent.toml`, it should delegate server management to `adj`. `adj agent-instructions` prints a markdown steering doc. Redirect it to the agent's instructions file:\n\n```sh\ncd path/to/your/app\nadj agent-instructions \u003e\u003e CLAUDE.md   # or AGENTS.md\n```\n\nThe doc names the app, names the dev command the agent should _not_ run, and lists the `adj` subcommands the agent should use to read state, restart, and verify changes.\n\nThe landing page sources live in `ent/`; `just serve` runs `npx live-server` against it.\n\n## Containers\n\n`cmd` can be a `docker run` as easily as an `npm run dev`. The command runs through a shell, so `$PORT` expands — map it to the container's port:\n\n```toml\nname = \"whoami\"\ncmd = \"docker run --rm --init --name adj-whoami -p 127.0.0.1:$PORT:80 traefik/whoami\"\nhealth_check_url = \"/\"\nboot_timeout = 120            # first boot may need to pull the image\n```\n\nThree things make this the shape that works:\n\n- **Run attached.** No `-d`, no `compose up -d`. Adjacent supervises the process it spawned; a detached `docker run` exits immediately and reads as a crash. Attached, the docker client forwards SIGTERM to the container on `adj down` and idle shutdown.\n- **Set `health_check_url`.** Docker binds the host port before the app inside is listening, so the default TCP-open probe reports ready too early. An HTTP check polls through to the app itself.\n- **`--rm` and `--init`.** `--rm` keeps stopped containers from piling up. `--init` makes SIGTERM reach the app even when the image's entrypoint doesn't forward signals.\n\nOne caveat: if a container ignores SIGTERM through the grace window, the follow-up SIGKILL kills the docker _client_ — the container keeps running under the Docker daemon. Naming it (`--name`) makes a leaked one easy to spot and `docker stop`.\n\n## Local Development\n\nToolchain pinned via `asdf` — see `.tool-versions` (rust 1.92.0, nodejs 26.2.0). Install the asdf plugins then run `asdf install` from the repo root.\n\n```sh\ncargo build                  # workspace build, binary at target/debug/adj\ncargo test                   # unit + integration tests\ncargo run -- daemon          # run the daemon in the foreground (Ctrl-C to stop)\n```\n\nIn another shell, against the running daemon:\n\n```sh\ncd path/to/your/app          # must contain adjacent.toml\ncargo run --manifest-path /path/to/adjacent/Cargo.toml -- add .\ncargo run --manifest-path /path/to/adjacent/Cargo.toml -- list\n```\n\nState lives in `~/.adjacent/`. Override with `ADJACENT_HOME=/tmp/adj-sandbox` to keep ad-hoc experiments out of the real home. Proxy port defaults to `8080`; override with `ADJACENT_PROXY_PORT=...`.\n\nMinimal `adjacent.toml`:\n\n```toml\nname = \"site\"\ncmd = \"npm run dev\"           # must bind to $PORT\n\n# Optional:\nhealth_check_url = \"/healthz\" # poll for 2xx instead of TCP-open\nidle_timeout = \"30m\"          # stop after no requests (default \"15m\", or \"off\")\n```\n\nThen `curl -H 'Host: site.adj.ac' http://127.0.0.1:8080/` lazy-boots the app and proxies through. Full `--json` output schema in [`crates/adj/JSON.md`](crates/adj/JSON.md).\n\n## License\n\nLicensed under either of the [Apache License, Version 2.0](LICENSE-APACHE) or the [MIT license](LICENSE-MIT) at your option.\n\nUnless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnonrational%2Fadjacent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnonrational%2Fadjacent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnonrational%2Fadjacent/lists"}