{"id":51145741,"url":"https://github.com/achetronic/tunnel","last_synced_at":"2026-06-26T02:31:17.687Z","repository":{"id":366917726,"uuid":"1259844260","full_name":"achetronic/tunnel","owner":"achetronic","description":"Expose private Kubernetes services to the internet in a simple, fast, secure and reliable way.","archived":false,"fork":false,"pushed_at":"2026-06-23T20:52:44.000Z","size":740,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-23T22:26:22.188Z","etag":null,"topics":["envoy","kubernetes","load-balancer","mtls","private-tunnel","tcp","tls","tunnel","udp","wireguard"],"latest_commit_sha":null,"homepage":"","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/achetronic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":null,"dco":null,"cla":null}},"created_at":"2026-06-04T23:17:48.000Z","updated_at":"2026-06-23T20:49:53.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/achetronic/tunnel","commit_stats":null,"previous_names":["achetronic/tunnel"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/achetronic/tunnel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/achetronic%2Ftunnel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/achetronic%2Ftunnel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/achetronic%2Ftunnel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/achetronic%2Ftunnel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/achetronic","download_url":"https://codeload.github.com/achetronic/tunnel/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/achetronic%2Ftunnel/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34801014,"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-26T02:00:06.560Z","response_time":106,"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":["envoy","kubernetes","load-balancer","mtls","private-tunnel","tcp","tls","tunnel","udp","wireguard"],"created_at":"2026-06-26T02:31:13.491Z","updated_at":"2026-06-26T02:31:17.676Z","avatar_url":"https://github.com/achetronic.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/images/header.svg\" alt=\"Tunnel\" width=\"760\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/achetronic/tunnel/blob/master/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/achetronic/tunnel?style=flat-square\u0026labelColor=0a0a0a\u0026color=27272a\" alt=\"License\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/achetronic/tunnel/blob/master/go.mod\"\u003e\u003cimg src=\"https://img.shields.io/github/go-mod/go-version/achetronic/tunnel?style=flat-square\u0026labelColor=0a0a0a\u0026color=27272a\u0026logo=go\u0026logoColor=white\" alt=\"Go version\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/achetronic/tunnel/releases\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/achetronic/tunnel?style=flat-square\u0026labelColor=0a0a0a\u0026color=27272a\u0026logo=github\u0026logoColor=white\" alt=\"Release\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/achetronic/tunnel/stargazers\"\u003e\u003cimg src=\"https://img.shields.io/github/stars/achetronic/tunnel?style=flat-square\u0026labelColor=0a0a0a\u0026color=27272a\u0026logo=github\u0026logoColor=white\" alt=\"Stars\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/achetronic\"\u003e\u003cimg src=\"https://img.shields.io/github/followers/achetronic?style=flat-square\u0026labelColor=0a0a0a\u0026color=27272a\u0026label=%40achetronic\u0026logo=github\u0026logoColor=white\" alt=\"achetronic\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nTunnel is a Kubernetes operator that exposes in-cluster TCP and UDP services on the public\nIP of a commodity VPS, for clusters that have no public ingress of their own: on-prem, behind\nCGNAT, or inside a locked-down VPC. The cluster dials out over WireGuard and the VPS relays\ninbound traffic back down that tunnel, so you never open a port on the private network or pay\nfor a managed load balancer.\n\n## How it works\n\nInbound traffic lands on a public port of the VPS and follows this path back into the cluster:\n\n```mermaid\nflowchart LR\n    I[\"🌐 Internet\"] --\u003e|\":443, :53, :NNNN\"| E[\"Envoy\u003cbr/\u003e(VPS edge)\"]\n    E --\u003e|\"N WireGuard tunnels\u003cbr/\u003edialed out by the cluster\"| U[\"Uplink pods\u003cbr/\u003eDNAT + masquerade\"]\n    U --\u003e S[\"ClusterIP Service\u003cbr/\u003eor raw IP:port\"]\n```\n\n- **Envoy** owns the public ports on the VPS. It proxies each one across the tunnels and\n  load-balances over the healthy uplinks, and can terminate TLS at the edge when you ask it\n  to.\n- **WireGuard** is the transport, always dialed out from the cluster, so nothing inbound is\n  ever opened on the private network.\n- **Uplink pods** terminate the tunnels inside the cluster and forward each port to its\n  target, a Service or a raw address. From there it is ordinary cluster networking.\n\nThe operator stays off this path. It enrolls the VPS over SSH, keeps the config in sync, and\notherwise leaves the host running stock, inspectable components.\n\n## The two resources\n\n**EdgeNode** is one VPS plus the secure tunnel back into your Kubernetes cluster. You declare\nthe address, the SSH credentials, the WireGuard overlay and the uplink details; the operator\nenrolls the host and wires everything up. It is the single writer over the VPS.\n\n**PortBinding** is where you declare what an EdgeNode exposes: which public ports to open,\nwhere each one routes inside the cluster (a Service or a raw `IP:port`), the per-protocol TCP\nand UDP settings, and how TLS is handled at the edge (passthrough, offload or mutual). Group\nthem in one PortBinding or spread them across several; changes apply live, without dropping\nconnections.\n\n## Using it\n\nThe order is always: create the SSH Secret, create an `EdgeNode`, then route ports with one\nor more `PortBinding` objects. The examples below climb from the bare minimum to a full setup.\n\n### The SSH Secret\n\nThe EdgeNode reads its credentials from a Secret. Use a `privateKey` (recommended) or a\n`password`, plus a `passphrase` if the key is encrypted:\n\n```bash\n# Private key\nkubectl create secret generic vps-ssh-secret \\\n  --namespace default \\\n  --from-file=privateKey=$HOME/.ssh/id_ed25519 \\\n  --from-file=knownHosts=\u003c(ssh-keyscan 203.0.113.10)\n\n# Or password\nkubectl create secret generic vps-ssh-secret \\\n  --namespace default \\\n  --from-literal=password='super-secret' \\\n  --from-file=knownHosts=\u003c(ssh-keyscan 203.0.113.10)\n```\n\nThe `knownHosts` entry (OpenSSH `known_hosts` format) pins the host key so the enrollment\nchannel cannot be MITM'd. Verification is on by default: without `knownHosts` the operator\nrefuses to connect unless you set `ssh.insecureSkipHostKeyVerification: true` on the\nEdgeNode.\n\n### Example 1: one TCP port\n\nThe smallest thing that works. The EdgeNode runs on defaults, so you only need the address\nand the Secret reference.\n\n```yaml\napiVersion: tunnel.achetronic.com/v1alpha1\nkind: EdgeNode\nmetadata:\n  name: edge-node\n  namespace: default\nspec:\n  address: 203.0.113.10\n  ssh:\n    secretRef:\n      name: vps-ssh-secret\n---\napiVersion: tunnel.achetronic.com/v1alpha1\nkind: PortBinding\nmetadata:\n  name: public-web\n  namespace: default\nspec:\n  edgeNodeRef:\n    name: edge-node\n  bindings:\n    - name: http\n      protocol: TCP\n      listenPort: 80\n      target:\n        service:\n          name: my-web\n          namespace: default\n          port: 8080\n```\n\nPort `:80` on the VPS now reaches the `my-web` Service. Everything else runs on defaults;\nExample 4 below shows the full EdgeNode with every knob spelled out.\n\n### Example 2: TCP and UDP, Service and raw IP\n\nOne PortBinding can carry several bindings. TCP can enable `proxyProtocol` (v1); UDP has its\nown `sessionTimeout` (keep it above the keepalive of whatever you tunnel); and a target is a\nService or a raw IP.\n\n```yaml\napiVersion: tunnel.achetronic.com/v1alpha1\nkind: PortBinding\nmetadata:\n  name: public-edge\n  namespace: default\nspec:\n  edgeNodeRef:\n    name: edge-node\n  bindings:\n    - name: https-web\n      protocol: TCP\n      listenPort: 443\n      tcp:\n        proxyProtocol: true # backend gets the real client IP\n      target:\n        service:\n          name: internal-ingress\n          namespace: networking\n          port: 443\n    - name: dns-udp\n      protocol: UDP\n      listenPort: 53\n      udp:\n        sessionTimeout: 60s\n      target:\n        address: 10.96.0.10 # straight to an IP, no Service lookup\n        port: 53\n```\n\n### Example 3: TLS at the edge\n\nA TCP binding can carry a `tls` block so Envoy handles TLS on the VPS. One `secretRef`\npoints at a standard `kubernetes.io/tls` Secret (what cert-manager produces); the `mode`\ndecides what the operator reads from it and what it does:\n\n| Mode          | What Envoy does                              | Reads from the Secret          | Key leaves the cluster? |\n| ------------- | -------------------------------------------- | ------------------------------ | ----------------------- |\n| `passthrough` | Routes by TLS SNI without decrypting         | nothing                        | No                      |\n| `offload`     | Terminates TLS on the VPS                    | `tls.crt`, `tls.key`           | Yes                     |\n| `mutual`      | Offload plus client-cert (mTLS) verification | `tls.crt`, `tls.key`, `ca.crt` | Yes                     |\n\n```yaml\napiVersion: tunnel.achetronic.com/v1alpha1\nkind: PortBinding\nmetadata:\n  name: public-tls\n  namespace: default\nspec:\n  edgeNodeRef:\n    name: edge-node\n  bindings:\n    # Route by SNI, the backend keeps terminating TLS. Nothing sensitive on the VPS.\n    - name: sni-passthrough\n      protocol: TCP\n      listenPort: 8443\n      tls:\n        mode: passthrough\n      target:\n        service:\n          name: internal-ingress\n          namespace: networking\n          port: 443\n\n    # Terminate TLS on the VPS. The cert's private key is copied to the edge.\n    - name: https-offload\n      protocol: TCP\n      listenPort: 9443\n      tls:\n        mode: offload\n        secretRef:\n          name: web-tls\n          namespace: default\n      target:\n        service:\n          name: my-web\n          namespace: default\n          port: 8080\n\n    # Offload plus verify client certificates against ca.crt from the same Secret.\n    - name: grpc-mtls\n      protocol: TCP\n      listenPort: 9444\n      tls:\n        mode: mutual\n        secretRef:\n          name: grpc-mtls\n          namespace: default\n      target:\n        service:\n          name: grpc-api\n          namespace: default\n          port: 50051\n```\n\nFor `offload` and `mutual` the operator copies the server private key to the VPS, so it\nemits a `PrivateKeyOnEdge` warning Event the first time it does. When cert-manager rotates\nthe Secret, the operator notices, re-pushes the cert, and reloads Envoy on its own.\n\n### Example 4: high availability and scheduling\n\nIn production you usually want more than one uplink replica. They run active-active, and\nEnvoy health-checks each one over the tunnel, so it only routes to replicas whose tunnel is\nup and drops a dead one on its own (if they all go down it fails fast instead of\nblack-holing). The EdgeNode is also where you pin the pods, tune the WireGuard network, and\nadjust that health checking under `edge`.\n\n```yaml\napiVersion: tunnel.achetronic.com/v1alpha1\nkind: EdgeNode\nmetadata:\n  name: edge-node\n  namespace: default\nspec:\n  address: 203.0.113.10\n  ssh:\n    port: 22\n    user: root\n    connectTimeout: 30s\n    secretRef:\n      name: vps-ssh-secret\n      namespace: default\n  edge:\n    healthCheck:\n      interval: 5s # time between /ready probes\n      timeout: 2s # per-probe timeout\n      healthyThreshold: 2 # consecutive successes before a replica is back in rotation\n      unhealthyThreshold: 2 # consecutive failures before a replica is taken out\n  tunnel:\n    listenPort: 51821\n    network: 10.200.0.0/24\n    mtu: 1420\n    persistentKeepalive: 25\n  uplink:\n    namespace: tunnel\n    replicas: 3\n    labels:\n      environment: production\n    annotations:\n      prometheus.io/scrape: \"true\"\n    resources:\n      requests:\n        cpu: \"100m\"\n        memory: \"128Mi\"\n      limits:\n        cpu: \"500m\"\n        memory: \"512Mi\"\n    nodeSelector:\n      node-role.kubernetes.io/edge: \"true\"\n    tolerations:\n      - key: \"dedicated\"\n        operator: \"Equal\"\n        value: \"edge\"\n        effect: \"NoSchedule\"\n    # affinity defaults to anti-affinity by hostname if you leave it out\n```\n\n### Good to know\n\n- Every `listenPort` is unique across all PortBindings pointing at the same EdgeNode, and\n  cannot clash with the tunnel's own `listenPort`. This is checked while the EdgeNode\n  reconciles, so a conflict surfaces as a failed EdgeNode reconcile, not a rejected\n  PortBinding.\n- Adding or removing bindings re-renders Envoy and reloads it in place; existing TCP\n  sessions survive.\n- A healthy EdgeNode re-reconciles every few minutes, not only on changes, so the operator\n  catches drift and keeps retrying if something on the VPS goes sideways.\n\n## Deploying the operator 🚀\n\nThe operator ships as a Helm chart (CRDs included) published as an OCI artifact. Install it:\n\n```bash\nhelm install tunnel oci://ghcr.io/achetronic/tunnel/charts/tunnel \\\n  --namespace tunnel --create-namespace \\\n  --version \u003cchart-version\u003e\n```\n\nSee [`values.yaml`](deploy/helm/tunnel/values.yaml) for the full surface.\nUpgrade with `helm upgrade ... --reuse-values`.\n\nOn [Kind](https://kind.sigs.k8s.io/), build and load both images first, then install with\n`--set image.tag` and `--set flags.imageTag` matching the loaded tag so the pods start without\npulling (`imagePullPolicy: IfNotPresent`).\n\n## Configuration ⚙️\n\nEverything is configured on the manager. There is nothing to set per resource beyond the\nCRDs themselves.\n\n### EdgeNode annotations\n\nDeliberate, operator-driven switches you set with `kubectl annotate edgenode \u003cname\u003e \u003ckey\u003e=true`:\n\n| Annotation                               | Effect                                                                                                                                                |\n| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `tunnel.achetronic.com/restart-envoy`    | Restart Envoy on the VPS on the next reconcile (how you apply a new Envoy version). One-shot: the operator does it and removes the annotation itself. |\n| `tunnel.achetronic.com/skip-deprovision` | On EdgeNode deletion, skip the SSH teardown of the VPS. The in-cluster uplink resources are still cleaned up.                                         |\n\n### Manager flags\n\n| Flag                                                                 | Default                      | What it does                                                                        |\n| -------------------------------------------------------------------- | ---------------------------- | ----------------------------------------------------------------------------------- |\n| `--envoy-version`                                                    | `1.29.3`                     | Envoy release installed on every managed VPS.                                       |\n| `--tunnelctl-dir`                                                    | `/opt/tunnelctl`             | Directory the operator reads the static tunnelctl binaries from to push to the VPS. |\n| `--image-repo`                                                       | `ghcr.io/achetronic/tunnel`  | Base repo for operator-managed images; the uplink image is `\u003crepo\u003e/uplink:\u003ctag\u003e`.   |\n| `--image-tag`                                                        | `latest`                     | Tag for those images; set it to the operator version so the uplink tag matches.     |\n| `--max-concurrent-reconciles`                                        | `5`                          | EdgeNodes reconciled in parallel; each speaks SSH to its own VPS.                    |\n| `--leader-elect`                                                     | `true`                       | Leader election so only one manager is active (HA).                                 |\n| `--leader-elect-namespace`                                           | `default`                    | Namespace for the leader election Lease; set it when running outside a cluster.     |\n| `--health-probe-bind-address`                                        | `:8081`                      | Address for the liveness/readiness probes.                                          |\n| `--metrics-bind-address`                                             | `0` (off)                    | Metrics address, e.g. `:8443` (HTTPS) or `:8080` (HTTP).                            |\n| `--metrics-secure`                                                   | `true`                       | Serve metrics over HTTPS with authn/authz.                                          |\n| `--enable-http2`                                                     | `false`                      | Enable HTTP/2 on the metrics and webhook servers.                                   |\n| `--metrics-cert-path` / `--metrics-cert-name` / `--metrics-cert-key` | `\"\"` / `tls.crt` / `tls.key` | Custom certificate for the metrics server.                                          |\n| `--webhook-cert-path` / `--webhook-cert-name` / `--webhook-cert-key` | `\"\"` / `tls.crt` / `tls.key` | Custom certificate for the webhook server.                                          |\n\n### Environment variables\n\nHonored by both binaries (manager and uplink):\n\n| Variable     | Default | Values                           | What it does                                                              |\n| ------------ | ------- | -------------------------------- | ------------------------------------------------------------------------- |\n| `LOG_FORMAT` | `json`  | `json`, `text`                   | Log output format; `text` is the readable console format for development. |\n| `LOG_LEVEL`  | `info`  | `debug`, `info`, `warn`, `error` | Minimum log level; `debug` shows the per-step enrollment detail.          |\n\n## Observability 🔭\n\nThe Envoy admin port is never exposed to the internet.\nEach uplink pod forwards its own `:40600` through the tunnel to Envoy's admin interface.\nThat bridge is the intended way to scrape metrics and debug;\nthere is deliberately no large status blob on the resources.\n\n```bash\n# Every uplink pod exposes Envoy's admin on its own pod IP at :40600, so any pod\n# in the cluster can reach it directly, no exec into the uplink needed.\nkubectl -n tunnel get pods -l app.kubernetes.io/name=uplink -o wide\n\n# Prometheus metrics, straight against an uplink pod IP\nkubectl run tmp --rm -it --restart=Never --image=curlimages/curl -- \\\n  curl -s http://\u003cuplink-pod-ip\u003e:40600/stats/prometheus\n\n# Same path for ad-hoc debugging (config_dump, clusters, ...)\nkubectl run tmp --rm -it --restart=Never --image=curlimages/curl -- \\\n  curl -s http://\u003cuplink-pod-ip\u003e:40600/config_dump\n```\n\nOr let Prometheus scrape the uplink pods directly with a PodMonitor:\n\n```yaml\napiVersion: monitoring.coreos.com/v1\nkind: PodMonitor\nmetadata:\n  name: tunnel-uplink-envoy\n  namespace: tunnel\nspec:\n  selector:\n    matchLabels:\n      app.kubernetes.io/name: uplink\n  podMetricsEndpoints:\n    - targetPort: 40600\n      path: /stats/prometheus\n      scheme: http\n```\n\nAny replica works; they all reach the same Envoy.\n\n## Contributing 🤝\n\nIssues and PRs are welcome. For a bug, say what you expected, what happened, and how to\nreproduce it. For a feature, explain the use case. Agreeing on the approach before writing\ncode is easier on everyone.\n\n### What you need\n\n- Go (the version pinned in `go.mod`), Docker, [Kind](https://kind.sigs.k8s.io/) and `kubectl`.\n- The rest of the tooling (`controller-gen`, `kustomize`, `setup-envtest`, `golangci-lint`) is\n  downloaded into `./bin` by the Makefile on first use, so there is nothing else to install.\n- The unit and integration tests run against a fake SSH layer and need no VPS. To exercise the\n  real data path you also need a throwaway Linux box the operator can reach over SSH.\n\n### Local loop with Kind\n\nThe manager runs on your host against a Kind cluster; the uplink workload runs inside it.\n\n```bash\nkind create cluster --name tunnel   # a throwaway cluster\nmake install                        # install the CRDs into it\nmake run                            # run the operator against the current kubeconfig\n```\n\nIn another shell, edit the samples (VPS address, SSH Secret, target Service) and apply them:\n\n```bash\nkubectl apply -f config/samples/tunnel_v1alpha1_edgenode.yaml\nkubectl apply -f config/samples/tunnel_v1alpha1_portbinding.yaml\nkubectl get edgenodes,portbindings -A\n```\n\nThe operator enrolls the VPS over SSH and creates the uplink StatefulSet. To run the full path\nlocally, build the uplink image and load it into Kind so the pods start without pulling, then\nrun the manager with the matching tag:\n\n```bash\nmake build-tunnelctl                                            # cross-compile the agent into ./bin/tunnelctl\nmake docker-build-uplink UPLINK_IMG=ghcr.io/achetronic/tunnel/uplink:dev\nkind load docker-image ghcr.io/achetronic/tunnel/uplink:dev --name tunnel\ngo run ./cmd/main.go --image-tag=dev --tunnelctl-dir=./bin/tunnelctl\n```\n\nTear it all down with `kind delete cluster --name tunnel`.\n\n### Tests and quality gate\n\n```bash\nmake test        # unit and integration tests (envtest); also runs manifests, generate, fmt, vet\nmake test-race   # the same suite under the race detector\nmake lint        # golangci-lint (make lint-fix to autofix)\nmake verify      # the full gate: fmt, vet, lint, test, test-race\n```\n\n`make test-e2e` spins up its own throwaway Kind cluster, runs the end-to-end suite and tears\nit down, so never point it at a cluster you care about. If you touch `api/**/*_types.go` or any\nkubebuilder marker, run `make manifests generate` to refresh the CRDs and generated code.\n\n### Issues and PRs\n\nOpen an issue first so we can agree on the change. Then send the PR from a branch on your\nfork: tidy commits, `make verify` green, and a link to the issue it closes (`Fixes #123`).\n\n### On using AI\n\nA lot of code and reviews these days are produced by AI. That is not how this project is run.\nLeaning on AI to help you write code, tests, or documentation is fine, and often genuinely\nuseful. Letting it lower the bar is not: do not ship code you have not read and understood,\nand do not open an issue or PR whose text is pasted from a model you never stopped to think\nthrough.\n\nIf you use AI, you still own what you submit. Read every line, make sure it is correct, and\nwrite your issues and PRs in your own words so a human can follow the reasoning. We treat this\ncodebase with the care a project like this deserves, so it improves over time, not the other\nway around. Quality is never traded for speed. AI is a great tool when it raises what you\nproduce, not when it stands in for understanding it.\n\nThe agentic context for this project lives in [`.agents/`](.agents/) and is kept current with\nevery iteration. If you work with an AI agent on this code, the first thing you tell it is:\n\nread the files in `.agents/` before doing anything.\n\n## License\n\nApache 2.0. See [LICENSE](LICENSE).\n\nCreated with ❤️ from the Canary Islands 🇮🇨\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fachetronic%2Ftunnel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fachetronic%2Ftunnel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fachetronic%2Ftunnel/lists"}