{"id":50126911,"url":"https://github.com/babykart/gozone","last_synced_at":"2026-07-01T22:01:12.627Z","repository":{"id":359843248,"uuid":"1247589387","full_name":"babykart/gozone","owner":"babykart","description":"A clean web interface for managing PowerDNS authoritative DNS servers","archived":false,"fork":false,"pushed_at":"2026-06-29T11:56:15.000Z","size":4792,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-29T13:22:42.546Z","etag":null,"topics":["api-rest","dns","dnssec","golang","pdns","tsig"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/babykart.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","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-05-23T14:15:38.000Z","updated_at":"2026-06-29T11:56:12.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/babykart/gozone","commit_stats":null,"previous_names":["babykart/gozone"],"tags_count":28,"template":false,"template_full_name":null,"purl":"pkg:github/babykart/gozone","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/babykart%2Fgozone","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/babykart%2Fgozone/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/babykart%2Fgozone/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/babykart%2Fgozone/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/babykart","download_url":"https://codeload.github.com/babykart/gozone/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/babykart%2Fgozone/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35024365,"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-07-01T02:00:05.325Z","response_time":130,"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":["api-rest","dns","dnssec","golang","pdns","tsig"],"created_at":"2026-05-23T20:04:58.451Z","updated_at":"2026-07-01T22:01:12.495Z","avatar_url":"https://github.com/babykart.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GoZone - PowerDNS Admin Interface in Go\n\n[![License](https://img.shields.io/badge/License-MIT-blue)](https://opensource.org/licenses/MIT)\n\nA clean web interface for managing PowerDNS authoritative DNS servers.\n\n## Features\n\n- **Zone Management**: List, create, edit, and delete DNS zones with pagination, search, and per-page controls\n- **Record Management**: Full CRUD for all DNS record types (A, AAAA, CNAME, MX, TXT, SOA, etc.) with color-coded type badges and inline editing\n- **RRSet Comments**: View, add, and edit PowerDNS comments per RRSet through the web UI, CSV import/export, and REST API\n- **Brute-force Protection**: Per-IP and per-username login rate-limiters (compound AND), persistent per-account lockout after repeated failures, audit trail of every attempt, identical generic error response across unknown user / wrong password / locked account to block account enumeration\n- **Multi-database Support**: SQLite (default), MySQL, and PostgreSQL are supported. Migrations are versioned by content hash with multi-instance locks.\n- **Zone Metadata**: Manage per-zone metadata (ALLOW-AXFR-FROM, ALSO-NOTIFY, SOA-EDIT, NSEC3PARAM, PRESIGNED, etc.)\n- **TSIG Keys**: Create, edit, and delete TSIG keys for secured zone transfers and dynamic updates\n- **Group-based Authorization**: Assign zones to groups, add users to groups — non-admin users see only their authorized zones\n- **User Management**: Admin and user roles with access control\n- **API Keys**: Generate and manage API keys for REST API access (SHA-256 hashed)\n- **Activity Logging**: Track all zone, metadata, TSIG key, and user operations\n- **REST API**: JSON API for zone, record, and statistics automation\n- **PowerDNS Integration**: Communicates through the PowerDNS REST API\n- **DNSSEC Support**: Zone rectification (manual + auto after key ops), slave notification\n- **Dark/Light Theme**: Toggle with localStorage persistence\n- **Single Binary**: Compiled Go binary with embedded templates and static files. Uses a local SQLite database by default; MySQL and PostgreSQL are also supported via configuration.\n- **Docker Support**: Ready-to-use Docker and docker-compose setup\n\n## Quick Start\n\n### Local Development\n\nDependencies are vendored — no download step required.\n\n```bash\n# Build and run\nmake run    # or: just run\n```\n\nOpen http://localhost:8080 — default admin credentials: `admin` / `admin`\n\n### Docker\n\n```bash\n# Start with docker-compose (includes PowerDNS)\nmake docker-up   # or: just docker-up\n\n# Or build and run standalone\nmake docker-build   # or: just docker-build\ndocker run -d -p 8080:8080 gozone\n```\n\n### Kubernetes (Helm)\n\nA Helm chart is available for deploying GoZone on Kubernetes:\n\n```bash\nhelm repo add babykart https://babykart.github.io/helm-charts\nhelm install gozone babykart/gozone\n```\n\nSee the [helm-charts repository](https://github.com/babykart/helm-charts) for configuration options.\n\n## Configuration\n\nConfiguration is via `config.yaml` or environment variables:\n\n### Server\n\n| YAML Path | Environment Variable | Default |\n|-----------|---------------------|---------|\n| `server.host` | `GOZONE_SERVER_HOST` | `0.0.0.0` |\n| `server.port` | `GOZONE_SERVER_PORT` | `8080` |\n| `server.app_name` | `GOZONE_APP_NAME` | `GoZone` |\n| `server.secret_key` | `GOZONE_SECRET_KEY` | *auto-generated* |\n| `server.secure_cookies` | `GOZONE_SECURE_COOKIES` | `false` |\n| `server.shutdown_timeout_seconds` | `GOZONE_SHUTDOWN_TIMEOUT` | `30` |\n| `server.trusted_proxies` | `GOZONE_TRUSTED_PROXIES` | *empty* (TCP source IP only) | Comma-separated CIDR ranges whose X-Forwarded-For headers are trusted. Use `/32` for a single IPv4 host, `/128` for IPv6. Plain IPs without a prefix are rejected at startup. |\n\n### Database\n\n| YAML Path | Environment Variable | Default |\n|-----------|---------------------|---------|\n| `database.driver` | `GOZONE_DB_DRIVER` | `sqlite3` |\n| `database.dsn` | `GOZONE_DB_DSN` | `./data/gozone.db` |\n\nSupported drivers: `sqlite3`, `mysql`, `postgres`. Database passwords in DSNs are automatically redacted in logs.\n\n### PowerDNS\n\n| YAML Path | Environment Variable | Default |\n|-----------|---------------------|---------|\n| `powerdns.api_url` | `GOZONE_PDNS_API_URL` | `http://localhost:8081` |\n| `powerdns.api_key` | `GOZONE_PDNS_API_KEY` | `changeme` |\n| `powerdns.server_id` | `GOZONE_PDNS_SERVER_ID` | `localhost` |\n\n### Authentication\n\n| YAML Path | Environment Variable | Default |\n|-----------|---------------------|---------|\n| `auth.session_duration_hours` | `GOZONE_SESSION_DURATION` | `24` |\n| `auth.bcrypt_cost` | — | `12` |\n\n### Login Lockout (brute-force protection)\n\n`/login` is always protected by an in-memory per-IP rate limiter (5/min). The knobs below add defence-in-depth against credential-stuffing and distributed brute-force:\n\n| YAML Path | Environment Variable | Default | Description |\n|-----------|---------------------|---------|-------------|\n| `login_lock.max_failed_attempts` | `GOZONE_LOGIN_MAX_FAILED_ATTEMPTS` | `10` | Consecutive failed attempts per account before lock. `0` disables persistent lockout (the IP/username rate limiters still protect the endpoint). |\n| `login_lock.lockout_duration_minutes` | `GOZONE_LOGIN_LOCKOUT_MINUTES` | `15` | How long the account stays locked. Every further failure extends the window so a sliding-window attack cannot recover. |\n| `login_lock.username_rate_limit_per_minute` | `GOZONE_LOGIN_USERNAME_RATE_PER_MINUTE` | `5` | In-memory per-username limiter, compounded with the per-IP limit at the route level. `0` disables. |\n| `login_lock.attempts_retention_hours` | `GOZONE_LOGIN_ATTEMPTS_RETENTION_HOURS` | `24` | How long a login attempt is kept in the `login_attempts` audit table before being purged. |\n\nThe client IP is resolved through chi's `ClientIPFrom*` middleware: by default `ClientIPFromRemoteAddr` (TCP source only, fail-closed against XFF/Real-IP spoofing). When `server.trusted_proxies` is configured with a list of CIDR ranges, the middleware switches to `ClientIPFromXFF` and walks XFF right-to-left until the first non-trusted hop. Leave `trusted_proxies` empty when GoZone is directly reachable on the public Internet — an attacker in direct access cannot rotate `X-Forwarded-For` to bypass the rate-limit.\n\nAll three authentication failure paths — unknown username, wrong password, locked account — return the **same** redirect target (`/login?error=invalid_credentials`) and the same generic banner (\"Invalid username or password.\"). The mapping is centralised in `loginErrorMessages` so the raw query code cannot leak into the rendered template, closing the account-enumeration channel that a different error message (e.g. `account_locked`) would otherwise expose. The constant-time dummy bcrypt compare on unknown users covers the timing channel; per-IP, per-username, and persistent account lockouts cover the rate channel.\n\n#### Recovering a locked admin\n\nWhen the only admin account is locked (brute-force storm, manual lock, lost credentials, etc.) the Web UI is no longer reachable: the `Unlock` button on `/admin/users` requires another non-locked admin to act. The emergency recovery path is the `gozone unlock` CLI subcommand, which opens the configured database directly and clears the lockout without going through the HTTP flow.\n\n```bash\n# Unlock by username (case-insensitive lookup; default config.yaml).\ngozone unlock --user admin\n\n# Unlock by numeric user ID — handy when the username is unknown.\ngozone unlock --user 1\n\n# Custom config path (e.g. when running outside the install directory).\ngozone unlock --user admin --config /etc/gozone/config.yaml\n```\n\nIn containerised deployments, exec into the running container first:\n\n```bash\n# Docker Compose (service name: gozone, see docker-compose.yml).\ndocker compose exec gozone gozone unlock --user admin\n\n# Kubernetes (adjust the deployment name and namespace).\nkubectl exec -n gozone deploy/gozone -- gozone unlock --user admin\n```\n\nThe action is **idempotent**: unlocking an already-unlocked user is a no-op on the database side but still writes an `unlock_user_cli` entry in the `activity_logs` table, so the audit trail always records the operator's intervention. Verify the unlock took effect by inspecting the user row:\n\n```bash\n# SQLite (default; adjust the path to match database.path in config.yaml)\nsqlite3 /var/lib/gozone/gozone.db \\\n  \"SELECT id, username, role, enabled, failed_login_attempts, locked_until FROM users WHERE id = 1;\"\n\n# PostgreSQL\npsql -U gozone -d gozone \\\n  -c \"SELECT id, username, role, enabled, failed_login_attempts, locked_until FROM users WHERE id = 1;\"\n\n# Recent unlock events from the audit trail\nsqlite3 /var/lib/gozone/gozone.db \\\n  \"SELECT id, user_id, action, created_at, substr(details, 1, 60) FROM activity_logs WHERE action LIKE 'unlock_user%' ORDER BY id DESC LIMIT 10;\"\n```\n\nCommon failure modes:\n\n| Error | Cause | Fix |\n|-------|-------|-----|\n| `--user is required (use --user \u003cid\u003e or --user \u003cusername\u003e)` | Flag omitted | Both `--user \u003cid\u003e` and `--user \u003cusername\u003e` are accepted. |\n| `user \"ghost\" not found` | Username typo or deleted account | Resolve the ID first with a `SELECT id, username FROM users` query. |\n| `open database: ...` | Binary cannot reach the configured DB | Check `--config` points at the right file and the process has read/write access to the data directory. |\n\n\u003e The CLI only clears the lockout — it does **not** reset the password. To recover a forgotten password without the Web UI, generate a bcrypt hash (GoZone uses `bcrypt.DefaultCost`) and run `UPDATE users SET password_hash = '\u003chash\u003e' WHERE username = '\u003cuser\u003e';` against the database.\n\n### Admin User (initial seed)\n\n| YAML Path | Environment Variable | Default |\n|-----------|---------------------|---------|\n| `admin.username` | `GOZONE_ADMIN_USERNAME` | `admin` |\n| `admin.password` | `GOZONE_ADMIN_PASSWORD` | `admin` |\n| `admin.email` | `GOZONE_ADMIN_EMAIL` | `admin@gozone.local` |\n| `admin.first_name` | `GOZONE_ADMIN_FIRST_NAME` | `Admin` |\n| `admin.last_name` | `GOZONE_ADMIN_LAST_NAME` | `User` |\n\n### Logging\n\n| YAML Path | Environment Variable | Default |\n|-----------|---------------------|---------|\n| `logging.level` | — | `info` |\n\n### Activity Log Retention\n\n| YAML Path | Environment Variable | Default |\n|-----------|---------------------|---------|\n| `activity.retention_days` | `GOZONE_ACTIVITY_RETENTION_DAYS` | `90` |\n| `activity.batch_size` | `GOZONE_ACTIVITY_BATCH_SIZE` | `1000` |\n\nSet `activity.retention_days` to `0` to keep activity logs indefinitely.\n\n### Secret Key\n\n**Important**: If no `server.secret_key` is set in the config file or via `GOZONE_SECRET_KEY`, a random 32-byte key is auto-generated at startup. For security the generated key is **not** written to the logs, so it cannot be recovered after startup — the key changes on every restart, invalidating all sessions and CSRF tokens. Always set `GOZONE_SECRET_KEY` or add `server.secret_key` to `config.yaml` for a stable key.\n\nThe master secret is split into independent JWT and CSRF sub-keys via HKDF-SHA256, so compromise of one sub-key does not reveal the other.\n\nTo generate a persistent key:\n```bash\nopenssl rand -hex 32\n```\n\n### HTTPS Configuration\n\nSession cookies use the `Secure` flag and `SameSite=Strict` by default. The `Secure` flag is automatically enabled when the request arrives over HTTPS (direct TLS or via `X-Forwarded-Proto: https` header from a reverse proxy).\n\nThe CSRF cookie's `Secure` flag is set once at startup and cannot be decided per request, so it is controlled by `server.secure_cookies` (`GOZONE_SECURE_COOKIES`). Set it to `true` whenever GoZone is served over HTTPS. Leave it `false` for plain-HTTP development, otherwise browsers will not return the CSRF cookie and form submissions will fail validation.\n\n**Option 1: Direct TLS**\n\nConfigure `server.port` to 443 and provide TLS certificate paths (requires a reverse proxy or Go TLS config).\n\n**Option 2: Reverse Proxy (recommended)**\n\nRun GoZone behind nginx, Caddy, or Traefik:\n\n```nginx\n# nginx example\nserver {\n    listen 443 ssl;\n    server_name dns-admin.example.com;\n\n    ssl_certificate     /etc/ssl/certs/example.com.pem;\n    ssl_certificate_key /etc/ssl/private/example.com.key;\n\n    location / {\n        proxy_pass http://127.0.0.1:8080;\n        proxy_set_header X-Forwarded-Proto https;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header Host $host;\n    }\n}\n```\n\n## Web UI\n\n### Dashboard\n\nShows PowerDNS server status (connected/unreachable, version, daemon type), zone and user counts, query statistics, and recent activity logs.\n\n### Zone View\n\nEach zone page displays:\n- **Records table** with color-coded type badges (A=blue, AAAA=violet, CNAME=orange, MX=pink, NS=cyan, etc.) and pagination by individual record rows (not RRSets), so the per-page limit always matches the number of visible table rows\n- **RRSet comments** — view, add, or edit PowerDNS comments per RRSet via the inline editor and dedicated edit page; one comment per line in the textarea\n- **DNSSEC management** — view, create, activate/deactivate, and delete DNSSEC keys (KSK/ZSK) with algorithm selection and DS record display\n- **Zone metadata** (admin only) — manage ALLOW-AXFR-FROM, ALSO-NOTIFY, SOA-EDIT, NSEC3PARAM, PRESIGNED, and other PowerDNS metadata kinds\n- **Activity logs** — history of changes to the zone\n- **Export** (BIND zone file or CSV) — download all zone records\n- **Import** (BIND or CSV) — upload and batch-create records into an existing zone\n- **Danger zone** (admin only) — delete zone, notify, rectify\n- **Clear cache** — invalidate the local zone list cache for the current zone, available to any user with group access to the zone\n\n### TSIG Keys\n\nManage TSIG keys for secured DNS operations (zone transfers, dynamic updates). Available to admin users under the TSIG Keys menu. Supports hmac-md5, hmac-sha1, hmac-sha256, and hmac-sha512 algorithms.\n\n### DNSSEC\n\nAdmin users can manage DNSSEC for each zone directly from the zone view page. Create KSK (Key Signing Key) and ZSK (Zone Signing Key) pairs with selectable algorithms (RSA/SHA-256, RSA/SHA-512, ECDSA P-256, ECDSA P-384, Ed25519, Ed448). Activate/deactivate keys, view DS records for parent zone configuration, and delete deactivated keys. All operations are logged in the activity feed. Zone rectification is triggered automatically after every key operation (create, toggle, delete) and is also available via the Rectify button (admin-only) in the zone header.\n\n### Export / Import\n\nExport full zone records in RFC 1035 BIND zone file format or CSV with a single click from the zone view page. Import zone data by uploading a `.zone` or `.csv` file — records are parsed and batch-created into the existing zone. Both are restricted to users with group access to the zone.\n\nImport uses PowerDNS `REPLACE` semantics: for each name+type pair in the file, the existing RRSet is replaced if present, or created if absent. Records not referenced in the import file are left untouched. Importing a file with fewer records than currently exist for a given name+type replaces the entire RRSet — extra existing records within that same name+type are removed.\n\n**Disabled records**: BIND export skips records marked `disabled` (the BIND format has no concept of disabled). CSV export keeps them with `disabled=true` so the round-trip is preserved.\n\n**RRSet comments**: CSV export adds a `comment` column at the end of the row. Multiple comments per RRSet are joined with newlines into a single cell (using standard CSV embedded-newline quoting) and the same cell is repeated on every record row of that RRSet. On import, the cell is split on newlines (one `Comment` per line) and deduplicated across rows of the same RRSet. CSV files without the `comment` column still parse unchanged.\n\n### RRSet Comments\n\nGoZone exposes the PowerDNS API `comments` field for every RRSet. Comments are metadata strings attached to a whole RRSet (not to individual records) and are useful for ops notes like \"managed by ops-team\" or \"created during migration XYZ\".\n\n- **Web UI**: a multi-line textarea (one comment per line) is available in the Add Records form, the batch create form, the dedicated Edit Record page, and the inline editor on the zone view. Existing comments are shown read-only under each record's content (styled as an italic blockquote).\n- **CSV export/import**: round-trips through the optional `comment` column described above.\n- **API**: pass a `comments` array in the RRSet payload when creating or updating records (see the API section below).\n- **PowerDNS semantics**: the PATCH `comments` field *replaces* the entire comment list for the RRSet. GoZone preserves this behaviour — when you edit a record without touching the comment field, the existing comments are kept (the field is omitted from the PATCH body); when you add or change a comment, existing comments are echoed back and your changes are applied.\n- **Clearing all comments**: the Edit Record page and the inline editor expose a \"Clear all comments\" checkbox. When checked, GoZone sends `\"comments\":[]` to PowerDNS, which deletes every existing comment for that RRSet. The checkbox only appears in the edit forms (not in the batch-create form, where no comments exist yet by default).\n\n### Zone Groups\n\nAdmin users can create groups, assign zones to groups, and add users as members. Non-admin users only see zones assigned to groups they belong to. The \"Groups\" link is visible in the sidebar for admin users.\n\n### Zone Templates\n\nAdmin users can define reusable DNS record templates that pre-populate records when creating new zones or applying to existing zones. Templates support variable substitution (`IP`, `IP6`, `MX_HOST`, `TTL`, `ZONE`, etc.) and include four built-in templates (standard, mail, web, redirect). Accessible under the Templates menu in the sidebar for admin users.\n\n### User Management\n\nAdmin users can create, edit, and delete user accounts from the **Users** menu in the sidebar. The list shows username, email, name, role, status (Active/Disabled/Locked), and per-row actions (Edit, Lock/Unlock, Delete).\n\n- **Self-DOS protection**: an admin cannot lock their own account from the UI; the self-lock attempt is rejected with a 400 error.\n- **Account lockout**: the `Lock` button sets `locked_until = now + login_lock.lockout_duration_minutes` and resets the failed-login counter, so a manual lock and the automatic failed-login threshold share the same window. Locked accounts show a yellow badge with a tooltip showing the unlock time.\n- **Unlock**: the `Unlock` button clears `locked_until` and resets the failed-login counter. The action is idempotent — unlocking a non-locked user still writes an `unlock_user` audit-log entry.\n- **Audit trail**: every lock and unlock writes an `activity_logs` entry with the actor's ID and the target's username.\n- **Last-admin guard**: the `UpdateUser` and `DeleteUser` handlers refuse to demote, disable, or delete the last enabled admin. The lock UI inherits this guard indirectly via the self-lock check (an admin cannot reach the lock button for the only admin).\n\n### API Keys\n\nUsers can generate personal API keys for programmatic access. Keys are SHA-256 hashed before storage — the raw key is shown only once at creation time.\n\n## API\n\nGoZone exposes a REST API under `/api/v1` for automation (zone and record CRUD, statistics, health). The complete endpoint reference — authentication, rate limiting, error envelope, every zone/record operation with payload schemas and curl examples — lives in [API.md](./docs/API.md).\n\n## Commands\n\n| Make | Just | Description |\n|------|------|-------------|\n| `make build` | `just build` | Build the binary |\n| `make run` | `just run` | Build and run locally |\n| `make test` | `just test` | Run tests |\n| `make test-verbose` | `just test-verbose` | Run tests with verbose output |\n| `make clean` | `just clean` | Remove build artifacts and database |\n| `make fmt` | `just fmt` | Format all source files |\n| `make vet` | `just vet` | Run vet on all packages |\n| `make gosec` | `just gosec` | Run security static analysis |\n| `make update` | `just update` | Update all dependencies |\n| `make docker-build` | `just docker-build` | Build Docker image |\n| `make docker-up` | `just docker-up` | Start services with docker-compose |\n| `make docker-down` | `just docker-down` | Stop services |\n\n## Building from Source\n\nRequirements: Go 1.26+. A C compiler (gcc/clang) is required only when building with the SQLite CGO driver; MySQL and PostgreSQL builds can use `CGO_ENABLED=0`.\n\n```bash\nmake build   # or: just build\n./bin/gozone -config config.yaml\n```\n\n## License\n\nMIT — see LICENSE file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbabykart%2Fgozone","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbabykart%2Fgozone","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbabykart%2Fgozone/lists"}