{"id":17604274,"url":"https://github.com/stuttgart-things/clusterbook","last_synced_at":"2026-04-01T19:40:51.628Z","repository":{"id":258122576,"uuid":"860014700","full_name":"stuttgart-things/clusterbook","owner":"stuttgart-things","description":"gitops configuration service","archived":false,"fork":false,"pushed_at":"2026-03-06T09:33:06.000Z","size":8000,"stargazers_count":0,"open_issues_count":12,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-06T13:52:07.258Z","etag":null,"topics":["golang","grpc","grpc-go","k8s"],"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/stuttgart-things.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2024-09-19T17:05:17.000Z","updated_at":"2025-07-24T11:20:32.000Z","dependencies_parsed_at":"2025-02-12T23:20:49.089Z","dependency_job_id":"cffe3237-8e05-4c9c-93de-616b018427ae","html_url":"https://github.com/stuttgart-things/clusterbook","commit_stats":null,"previous_names":["stuttgart-things/clusterbook"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/stuttgart-things/clusterbook","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stuttgart-things%2Fclusterbook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stuttgart-things%2Fclusterbook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stuttgart-things%2Fclusterbook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stuttgart-things%2Fclusterbook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stuttgart-things","download_url":"https://codeload.github.com/stuttgart-things/clusterbook/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stuttgart-things%2Fclusterbook/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30459817,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-13T03:55:51.346Z","status":"ssl_error","status_checked_at":"2026-03-13T03:55:33.055Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["golang","grpc","grpc-go","k8s"],"created_at":"2024-10-22T14:07:43.257Z","updated_at":"2026-04-01T19:40:51.607Z","avatar_url":"https://github.com/stuttgart-things.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# stuttgart-things/clusterbook\n\ngitops cluster configuration management\n\n\u003cdiv align=\"center\"\u003e\n  \u003cp\u003e\n    \u003cimg src=\"https://github.com/stuttgart-things/docs/blob/main/hugo/sthings-argo.png\" alt=\"sthings\" width=\"450\" /\u003e\n  \u003c/p\u003e\n  \u003cp\u003e\n    \u003cstrong\u003e[/ˈklʌstəʳbʊk/]\u003c/strong\u003e- gitops cluster configuration management\n\n  \u003c/p\u003e\n\u003c/div\u003e\n\n## FEATURES\n\n| Feature | Description |\n|---------|-------------|\n| IP Address Management | Allocate and track IPs across Kubernetes clusters |\n| CIDR-aware Allocation | Define pools as CIDR ranges (e.g. `10.31.103.0/24`) with auto-expansion |\n| gRPC API | Programmatic access on port `:50051` |\n| REST API | JSON endpoints on port `:8080` |\n| HTMX Dashboard | Web UI for IP pool visualization and management |\n| Dual Storage | Filesystem (YAML) or Kubernetes CRD backend |\n| PowerDNS Integration | Optional DNS record management for IP assignments |\n| DD-WRT Integration | Optional DNS via DD-WRT router (SSH + dnsmasq) |\n| KCL Manifests | Type-safe Kubernetes deployment with KCL |\n\n## DEPLOYMENT\n\n\u003cdetails\u003e\u003csummary\u003eKCL (RECOMMENDED)\u003c/summary\u003e\n\n### Render manifests\n\n```bash\ncd kcl \u0026\u0026 kcl run\n```\n\n### Render with custom config\n\n```bash\nkcl run -D config.image=ghcr.io/stuttgart-things/clusterbook:v1.6.0 \\\n        -D config.namespace=clusterbook \\\n        -D config.configName=networks-labul\n```\n\n### Render with HTTPRoute (Gateway API)\n\n```bash\nkcl run -D config.httpRouteEnabled=true \\\n        -D config.httpRouteParentRefName=my-gateway \\\n        -D config.httpRouteHostname=clusterbook.example.com\n```\n\n### Apply to cluster\n\n```bash\ncd kcl \u0026\u0026 kcl run | kubectl apply -f -\n```\n\n\u003c/details\u003e\n\n## LOCAL DEVELOPMENT\n\n\u003cdetails\u003e\u003csummary\u003eRUN LOCALLY\u003c/summary\u003e\n\n### From disk config (easiest)\n\n```bash\nLOAD_CONFIG_FROM=disk CONFIG_LOCATION=tests CONFIG_NAME=config.yaml go run .\n```\n\n### From Kubernetes CR (requires cluster access)\n\n```bash\nLOAD_CONFIG_FROM=cr CONFIG_LOCATION=clusterbook CONFIG_NAME=networks-labul go run .\n```\n\n### Using Taskfile + .env\n\nCreate a `.env` file (see [example below](#example-env-file)), then:\n\n```bash\ntask run\n```\n\n### Quick web UI start (no .env needed)\n\n```bash\ntask run-web\n```\n\nThis runs the web UI with disk config from `tests/config.yaml` on port `8080`. Override defaults with:\n\n```bash\ntask run-web CONFIG_LOCATION=mydir CONFIG_NAME=myconfig.yaml HTTP_PORT=9090\n```\n\n\u003c/details\u003e\n\n## USAGE\n\n\u003cdetails\u003e\u003csummary\u003eWEB UI (HTMX)\u003c/summary\u003e\n\nThe HTMX dashboard is available on port `:8080` (configurable via `HTTP_PORT`).\n\n- **Dashboard**: `http://localhost:8080/` - overview of all network pools\n- **Network Detail**: `http://localhost:8080/network/10.31.103` - IP table with inline assign/release\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eREST API\u003c/summary\u003e\n\n### List all network pools\n\n```bash\ncurl http://localhost:8080/api/v1/networks\n```\n\n### List IPs in a network\n\n```bash\ncurl http://localhost:8080/api/v1/networks/10.31.103/ips\n```\n\n### Assign an IP\n\n```bash\ncurl -X POST http://localhost:8080/api/v1/networks/10.31.103/assign \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"ip\": \"10.31.103.6\", \"cluster\": \"my-cluster\", \"status\": \"ASSIGNED\"}'\n```\n\n### Release an IP\n\n```bash\ncurl -X POST http://localhost:8080/api/v1/networks/10.31.103/release \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"ip\": \"10.31.103.6\"}'\n```\n\n### Create a network from CIDR\n\n```bash\n# Creates a /24 pool with 252 usable IPs (excludes .0, .1, .2, .255)\ncurl -X POST http://localhost:8080/api/v1/networks/cidr \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"cidr\": \"10.31.105.0/24\", \"reserved\": [\"1\", \"2\"]}'\n```\n\nThe existing `POST /api/v1/networks` endpoint also accepts CIDR:\n\n```bash\ncurl -X POST http://localhost:8080/api/v1/networks \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"cidr\": \"10.31.105.0/28\"}'\n```\n\nCIDR ranges spanning multiple /24 blocks (e.g. `/23`) automatically create multiple network entries.\n\n### Assign an IP with DNS\n\n```bash\ncurl -X POST http://localhost:8080/api/v1/networks/10.31.103/assign \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"ip\": \"10.31.103.6\", \"cluster\": \"my-cluster\", \"status\": \"ASSIGNED\", \"create_dns\": true}'\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003egRPC\u003c/summary\u003e\n\n```bash\n# Get available IPs\ngrpcurl -plaintext localhost:50051 ipservice.IpService/GetIpAddressRange \\\n  -d '{\"countIpAddresses\": 2, \"networkKey\": \"10.31.103\"}'\n\n# Assign IPs to a cluster\ngrpcurl -plaintext localhost:50051 ipservice.IpService/SetClusterInfo \\\n  -d '{\"ipAddressRange\": \"10.31.103.6\", \"clusterName\": \"my-cluster\", \"status\": \"ASSIGNED\"}'\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eDAGGER MODULE\u003c/summary\u003e\n\nA [Dagger](https://dagger.io) module is available at [stuttgart-things/dagger/clusterbook](https://github.com/stuttgart-things/dagger) for pipeline integration.\n\n```bash\n# List all networks\ndagger call list-networks --server=\"clusterbook.example.com:8080\"\n\n# Create network from CIDR\ndagger call create-network-from-cidr --server=\"localhost:8080\" --cidr=\"10.31.105.0/24\" --reserved=\"1\"\n\n# Assign IP with DNS\ndagger call assign-ip --server=\"localhost:8080\" --network-key=\"10.31.103\" \\\n  --ip=\"10.31.103.6\" --cluster=\"my-cluster\" --status=\"ASSIGNED\" --create-dns\n\n# Release IP\ndagger call release-ip --server=\"localhost:8080\" --network-key=\"10.31.103\" --ip=\"10.31.103.6\"\n\n# List clusters\ndagger call list-clusters --server=\"localhost:8080\"\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eCLI (machineshop)\u003c/summary\u003e\n\n### GET IPS\n\n```bash\nmachineshop get \\\n--system=ips \\\n--destination=clusterbook.172.18.0.5.nip.io \\\n--path=10.31.103 \\\n--output=2\n```\n\n```bash\nmachineshop push \\\n--target=ips \\\n--destination=clusterbook.172.18.0.5.nip.io \\\n--artifacts=\"10.31.103.9;10.31.103.10\" \\\n--assignee=app1\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eCREATE CR\u003c/summary\u003e\n\n```bash\nkubectl apply -f - \u003c\u003cEOF\n---\napiVersion: github.stuttgart-things.com/v1\nkind: NetworkConfig\nmetadata:\n  name: networks-labul\n  namespace: clusterbook\nspec:\n  networks:\n    10.31.101:\n    - 6:ASSIGNED:rahul-andre-rke2\n    - \"7\"\n    - \"9\"\n    - \"10\"\n    - 5:ASSIGNED:rancher-mgmt\n    10.31.102:\n    - \"5\"\n    - \"6\"\n    - \"7\"\n    - 8:ASSIGNED:unknown\n    - \"9\"\n    - \"10\"\n    10.31.103:\n    - 4:ASSIGNED:homerun-int2\n    - 5:ASSIGNED:labul-automation\n    - 6:ASSIGNED:labul-automation\n    - \"17\"\n    - \"18\"\n    - 19:ASSIGNED:labul-automation\n    - 8:ASSIGNED:fluxdev-3\n    - 9:ASSIGNED:fluxdev-3\n    - 16:ASSIGNED:homerun-dev\n    10.31.104:\n    - \"5\"\n    - \"6\"\n    - \"7\"\n    - \"8\"\n    - \"9\"\n    - \"10\"\nEOF\n```\n\n\u003c/details\u003e\n\n## CONFIGURATION\n\n| Environment Variable | Description | Default |\n|---------------------|-------------|---------|\n| `LOAD_CONFIG_FROM` | Config source: `disk` or `cr` | - |\n| `CONFIG_LOCATION` | File path or K8s namespace | - |\n| `CONFIG_NAME` | File name or resource name | - |\n| `SERVER_PORT` | gRPC server port | `50051` |\n| `HTTP_PORT` | HTTP/HTMX server port | `8080` |\n| `KUBECONFIG` | K8s config path (for CR backend) | - |\n| `PDNS_ENABLED` | Enable PowerDNS integration | `false` |\n| `PDNS_URL` | PowerDNS API URL | - |\n| `PDNS_TOKEN` | PowerDNS API token | - |\n| `PDNS_ZONE` | PowerDNS zone for records | - |\n| `DDWRT_ENABLED` | Enable DD-WRT DNS integration | `false` |\n| `DDWRT_HOST` | DD-WRT router IP/hostname | - |\n| `DDWRT_USER` | SSH user for DD-WRT | - |\n| `DDWRT_PASSWORD` | SSH password for DD-WRT | - |\n| `DDWRT_ZONE` | DNS zone (e.g. `sthings.lab`) | - |\n\n## DNS PROVIDERS\n\nBoth DNS providers are optional and can run simultaneously. Enable them via env vars. Records are created/deleted when `create_dns: true` is passed during assign/release.\n\n\u003cdetails\u003e\u003csummary\u003ePOWERDNS\u003c/summary\u003e\n\nCreates wildcard A records (`*.cluster.zone`) via the PowerDNS REST API.\n\n```bash\nPDNS_ENABLED=true\nPDNS_URL=http://pdns.sthings.lab:8081\nPDNS_TOKEN=your-api-key\nPDNS_ZONE=sthings.lab.\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eDD-WRT\u003c/summary\u003e\n\nManages `dnsmasq` address entries on a DD-WRT router via SSH + `nvram`. Creates entries like `address=/cluster.zone/ip`.\n\n```bash\nDDWRT_ENABLED=true\nDDWRT_HOST=192.168.1.1\nDDWRT_USER=root\nDDWRT_PASSWORD=your-router-password\nDDWRT_ZONE=sthings.lab\n```\n\n**Credential handling in Kubernetes:**\n\n```yaml\napiVersion: v1\nkind: Secret\nmetadata:\n  name: clusterbook-ddwrt\ntype: Opaque\nstringData:\n  DDWRT_ENABLED: \"true\"\n  DDWRT_HOST: \"192.168.1.1\"\n  DDWRT_USER: \"root\"\n  DDWRT_PASSWORD: \"your-router-password\"\n  DDWRT_ZONE: \"sthings.lab\"\n```\n\nReference in the deployment:\n\n```yaml\nenvFrom:\n  - secretRef:\n      name: clusterbook-ddwrt\n```\n\n\u003c/details\u003e\n\n## DEV TASKS\n\n```bash\ntask: Available tasks for this project:\n* branch:         Create branch from main\n* build:          Install\n* build-ko:       Build image w/ KO\n* commit:         Commit + push code into branch\n* lint:           Lint Golang\n* pr:             Create pull request into main\n* proto:          Generate Go code from proto\n* run:            Run\n* test:           Test code\n```\n\n## AUTHOR\n\n```bash\nPatrick Hermann, stuttgart-things 09/2024\n```\n\n## EXAMPLE .env file\n\n\u003cdetails\u003e\u003csummary\u003eENV FILE\u003c/summary\u003e\n\n.env file needed for Taskfile\n\n```bash\ncat \u003c\u003cEOF \u003e .env\n#LOAD_CONFIG_FROM=disk\n#CONFIG_LOCATION=tests\n#CONFIG_NAME=config.yaml\nLOAD_CONFIG_FROM=cr\nCONFIG_LOCATION=clusterbook #namespace\nCONFIG_NAME=networks-labul #resource-name\n\nSERVER_PORT=50051\nHTTP_PORT=8080\n\n# DD-WRT DNS (optional)\n#DDWRT_ENABLED=true\n#DDWRT_HOST=192.168.1.1\n#DDWRT_USER=root\n#DDWRT_PASSWORD=secret\n#DDWRT_ZONE=sthings.lab\n\n#CLUSTERBOOK_SERVER=localhost:50051\n#SECURE_CONNECTION=false\nCLUSTERBOOK_SERVER=clusterbook.rke2.sthings-vsphere.labul.sva.de:443\nSECURE_CONNECTION=true\nEOF\n```\n\n\u003c/details\u003e\n\n## LICENSE\n\nLicensed under the Apache License, Version 2.0 (the \"License\").\n\nYou may obtain a copy of the License at [apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0).\n\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an _\"AS IS\"_ basis, without WARRANTIES or conditions of any kind, either express or implied.\n\nSee the License for the specific language governing permissions and limitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstuttgart-things%2Fclusterbook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstuttgart-things%2Fclusterbook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstuttgart-things%2Fclusterbook/lists"}