{"id":44138538,"url":"https://github.com/silo-rs/silo","last_synced_at":"2026-02-15T02:00:43.428Z","repository":{"id":337314676,"uuid":"1153017930","full_name":"silo-rs/silo","owner":"silo-rs","description":"📡 localhost multiplexer","archived":false,"fork":false,"pushed_at":"2026-02-13T23:16:14.000Z","size":650,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-14T05:08:14.428Z","etag":null,"topics":["agent","ai-agents","claude","cli","cursor","developer-tools","devx","git","localhost","port","rust","worktree"],"latest_commit_sha":null,"homepage":"https://silo.rs","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/silo-rs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-08T19:22:18.000Z","updated_at":"2026-02-13T23:16:17.000Z","dependencies_parsed_at":"2026-02-14T01:03:21.378Z","dependency_job_id":null,"html_url":"https://github.com/silo-rs/silo","commit_stats":null,"previous_names":["silo-rs/silo"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/silo-rs/silo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/silo-rs%2Fsilo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/silo-rs%2Fsilo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/silo-rs%2Fsilo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/silo-rs%2Fsilo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/silo-rs","download_url":"https://codeload.github.com/silo-rs/silo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/silo-rs%2Fsilo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29465397,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T01:01:38.065Z","status":"online","status_checked_at":"2026-02-15T02:00:07.449Z","response_time":118,"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":["agent","ai-agents","claude","cli","cursor","developer-tools","devx","git","localhost","port","rust","worktree"],"created_at":"2026-02-09T00:12:16.207Z","updated_at":"2026-02-15T02:00:43.411Z","avatar_url":"https://github.com/silo-rs.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003esilo\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003eRun the same app, on the same port, at the same time.\u003c/b\u003e\u003cbr\u003e\n  Zero config. No containers. No code changes.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"demo.gif\" width=\"100%\" /\u003e\n\u003c/p\u003e\n\n## Why\n\nYou're running 3 AI agents on 3 features. All need `localhost:3000`. Port taken.\n\nOr maybe it's just you, switching between branches. You know the drill -- `lsof -i :3000`, find the PID, `kill -9`, try again.\n\n| Approach              | Problem                                   |\n| --------------------- | ----------------------------------------- |\n| Change ports manually | Requires code/config changes per instance |\n| Docker                | Heavy, slow, breaks native toolchains     |\n| Run one at a time     | Kills your workflow                       |\n\nsilo takes a different approach: intercept `bind()` at the syscall level and give each directory its own loopback IP. Your app calls `bind(\"0.0.0.0\", 3000)` -- silo rewrites it to `bind(\"127.1.x.x\", 3000)` before it hits the kernel. No code changes.\n\n## Quick start\n\n```sh\ncurl -fsSL https://setup.silo.rs | sh\n```\n\n```sh\ncd ~/projects/acme\nsilo npm run dev\n# silo ● main\n#      127.1.42.7 · main.acme.silo\n# Listening on http://localhost:3000\n```\n\nIn another terminal, on a different branch:\n\n```sh\ncd ~/worktrees/feature\nsilo npm run dev\n# silo ● feature\n#      127.1.183.12 · feature.acme.silo\n# Listening on http://localhost:3000\n```\n\nBoth on port 3000. No conflict. Each gets its own IP and hostname.\n\n## How it works\n\n```\nyour app → bind(\"0.0.0.0:3000\") → [ silo intercepts ] → bind(\"127.1.42.7:3000\") ✅\n```\n\n`silo` does four things:\n\n1. **Computes a deterministic IP** from your git root path (FNV-1a hash → `127.1.x.x`)\n2. **Creates a loopback alias** for that IP (`ifconfig lo0 alias` / `ip addr add`)\n3. **Registers a hostname** in `/etc/hosts` (e.g. `main.myapp.silo` → `127.1.x.x`)\n4. **Injects a shared library** (`DYLD_INSERT_LIBRARIES` / `LD_PRELOAD`) that rewrites `bind()`, `connect()`, `getaddrinfo()`, `gethostbyname()`, `sendto()`, and more to use that IP\n\nThe IP is a pure function of your directory path -- deterministic and stable across reboots.\n\n## Environment variables\n\nThese are automatically set inside every silo session:\n\n| Variable    | Description               | Example                    |\n| ----------- | ------------------------- | -------------------------- |\n| `SILO_IP`   | Deterministic loopback IP | `127.1.42.7`               |\n| `SILO_NAME` | Sanitized branch name     | `feature-auth`             |\n| `SILO_DIR`  | Git root path             | `/home/user/my-app`        |\n| `SILO_HOST` | Hostname                  | `feature-auth.my-app.silo` |\n\n## Commands\n\n| Command       | Description                                |\n| ------------- | ------------------------------------------ |\n| `silo \u003ccmd\u003e`  | Run command with transparent IP isolation  |\n| `silo ip`     | Show the resolved IP for current directory |\n| `silo ls`     | List active silo sessions                  |\n| `silo prune`  | Remove unused aliases and /etc/hosts entries |\n| `silo doctor` | Diagnose environment issues                |\n\n### Options\n\n```\nsilo [OPTIONS] \u003cCOMMAND\u003e...\n\nOptions:\n  -n, --name \u003cNAME\u003e   Override name (default: git branch)\n  -q, --quiet         Suppress the silo banner\n```\n\n`silo run \u003ccmd\u003e` also works as an explicit form.\n\n## Deep dive\n\n### What gets rewritten\n\n| Syscall                              | Original address                 | Rewritten to | Why                                                |\n| ------------------------------------ | -------------------------------- | ------------ | -------------------------------------------------- |\n| `bind()`                             | `0.0.0.0` or `127.0.0.1`         | `SILO_IP`    | Server listens on its own loopback IP              |\n| `connect()`                          | `127.0.0.1`                      | `SILO_IP`    | Client talks to its own server, not someone else's |\n| `getaddrinfo()`                      | Results resolving to `127.0.0.1` | `SILO_IP`    | DNS-based localhost lookups get the same treatment |\n| `gethostbyname()` / `gethostbyname2()` | Results resolving to `127.0.0.1` | `SILO_IP`    | Legacy DNS lookups don't leak to the wrong IP      |\n| `sendto()`                           | `0.0.0.0` or `127.0.0.1`         | `SILO_IP`    | UDP traffic goes to the right place                |\n| `sendmsg()`                          | `0.0.0.0` or `127.0.0.1`         | `SILO_IP`    | Datagram messages go to the right place            |\n| `getifaddrs()` (macOS)               | Other silo aliases               | Hidden       | Each session only sees its own loopback alias      |\n\nOn macOS, IPv6 sockets binding to `::` or `::1` are **downgraded to IPv4** and rewritten to `SILO_IP`. On Linux, they're rewritten to the IPv4-mapped IPv6 address (`::ffff:SILO_IP`).\n\nAnything else -- specific IPs like `192.168.x.x`, Unix domain sockets, non-loopback addresses -- passes through untouched.\n\n### Multi-repo services\n\nIn a monorepo, all services share the same `SILO_IP` -- cross-service `localhost` calls just work.\n\nFor separate repos (e.g. `frontend` + `backend`), use `.silo` hostnames as service discovery. Each silo session registers a hostname in `/etc/hosts` with the format `{branch}.{repo}.silo`:\n\n```sh\n# frontend repo's .env\nBACKEND_URL=http://${SILO_NAME}.backend.silo:4000\n```\n\nOn the `main` branch this resolves to `main.backend.silo` → the backend's silo IP. Switch to `feature-auth` and it becomes `feature-auth.backend.silo` -- automatically pointing to the right instance.\n\nThis works because silo only rewrites `127.0.0.1` -- connections to other silo IPs (like `127.1.x.x` resolved from a `.silo` hostname) pass through untouched.\n\n### Edge cases\n\n**Statically linked binaries**\n\n`DYLD_INSERT_LIBRARIES` / `LD_PRELOAD` only works with dynamically linked binaries. Statically compiled programs (common in Go) bypass the interception entirely. For Go specifically, compile with `CGO_ENABLED=1` to use libc, or configure the app to bind to `$SILO_IP` directly.\n\n**macOS System Integrity Protection (SIP)**\n\nmacOS prevents library injection into system binaries under `/usr/bin`, `/bin`, `/sbin`. Silo handles this automatically by detecting SIP-protected paths and finding non-SIP alternatives (e.g. Homebrew-installed `bash` instead of `/bin/bash`).\n\n### Debugging\n\nSet `SILO_BIND_DEBUG=1` to see every intercepted syscall:\n\n```sh\nSILO_BIND_DEBUG=1 silo npm run dev\n# [silo-bind] loaded pid=12345 SILO_IP=127.1.42.7\n# [silo-bind] pid=12345 bind fd=7 family=2 port=3000 SILO_IP=127.1.42.7\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsilo-rs%2Fsilo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsilo-rs%2Fsilo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsilo-rs%2Fsilo/lists"}