{"id":51600945,"url":"https://github.com/seventhsite/yourls-domain-allowlist","last_synced_at":"2026-07-31T00:00:42.872Z","repository":{"id":367969220,"uuid":"1282963769","full_name":"seventhsite/yourls-domain-allowlist","owner":"seventhsite","description":"Restrict URL shortening to an allowlist of domains, with an admin page and throttled Telegram alerts about rejected domains. No extra DB table, no cron. Drop-in compatible with nicwaller/yourls-domainlimit.","archived":false,"fork":false,"pushed_at":"2026-06-28T13:31:55.000Z","size":320,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-28T14:22:43.327Z","etag":null,"topics":["allowlist","domain-restriction","php","telegram","url-shortener","whitelist","yourls","yourls-plugin"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seventhsite.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-28T11:56:39.000Z","updated_at":"2026-06-28T13:31:58.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/seventhsite/yourls-domain-allowlist","commit_stats":null,"previous_names":["seventhsite/yourls-domain-allowlist"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/seventhsite/yourls-domain-allowlist","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seventhsite%2Fyourls-domain-allowlist","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seventhsite%2Fyourls-domain-allowlist/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seventhsite%2Fyourls-domain-allowlist/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seventhsite%2Fyourls-domain-allowlist/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seventhsite","download_url":"https://codeload.github.com/seventhsite/yourls-domain-allowlist/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seventhsite%2Fyourls-domain-allowlist/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":36096156,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-07-30T02:00:05.956Z","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":["allowlist","domain-restriction","php","telegram","url-shortener","whitelist","yourls","yourls-plugin"],"created_at":"2026-07-11T22:00:32.914Z","updated_at":"2026-07-31T00:00:42.866Z","avatar_url":"https://github.com/seventhsite.png","language":"PHP","funding_links":[],"categories":["Plugins"],"sub_categories":["D"],"readme":"# Domain Allowlist + Alerts (YOURLS plugin)\n\n[![Listed in Awesome YOURLS!](https://img.shields.io/static/v1?label=Awesome\u0026message=YOURLS\u0026color=C5A3BE\u0026style=flat-square)](https://github.com/YOURLS/awesome)\n\nRestrict URL shortening to an allowlist of domains, and — optionally — get a\nTelegram heads-up when someone tries to shorten a domain that **isn't** on the\nlist. That rejected-domain signal tells you which destinations your users\nactually want, so you can grow the allowlist on demand instead of guessing.\n\nSelf-contained by design: **no extra database table, no cron job, no server\nconfiguration.** Rejected-domain stats live in a single size-bounded JSON file,\nand the periodic digest piggybacks on normal site traffic (\"lazy cron\").\n\n![The plugin's admin page — the effective allowlist, the live list of rejected domains with one-click \"Add to allowlist\", and the alert settings.](screenshots/admin.png)\n\n## Features\n\n- Allow shortening only for hosts on a configurable list. Matching is exact host\n  **or any subdomain** — `example.com` also matches `www.example.com` and\n  `go.example.com`, but not `notexample.com`.\n- **Drop-in config compatibility** with the classic\n  [nicwaller/yourls-domainlimit](https://github.com/nicwaller/yourls-domainlimit-plugin):\n  it reuses `$domainlimit_list` and `$domainlimit_exempt_users`, so existing\n  installs can switch with no config changes.\n- Optional **Telegram alerts** about rejected domains:\n  - `instant` — the first time a new domain is rejected (throttled), and/or\n  - `digest` — a periodic \"top rejected domains\" summary.\n- **Anti-spam / anti-ban throttling**: a per-domain cooldown, a global minimum\n  gap between any two sends, and short network timeouts — all fire-and-forget,\n  so a slow or failing Telegram call never blocks or breaks a shorten request.\n- Returns structured data (`disallowed_host`, `allowed_domains`) in the error\n  result so a custom theme can render a friendly \"domain not allowed\" page.\n- **Admin settings page** (Plugins → Domain Allowlist): edit an extra allowlist,\n  one-click-allow domains straight from the list of rejected attempts, manage the\n  Telegram settings, and send a test message — all without touching a file.\n- PHP 8.x clean. No external dependencies.\n\n## Install\n\n1. Put `plugin.php` in a folder under `user/plugins/` (e.g. `user/plugins/domain-allowlist/`).\n2. Activate **Domain Allowlist + Alerts** on the YOURLS admin Plugins page.\n3. Configure it in `user/config.php` (below) **or** on the plugin's admin\n   settings page (Plugins → Domain Allowlist).\n\n## Configuration (`user/config.php`)\n\n```php\n// Required — the allowed domains (exact host or any subdomain).\n$domainlimit_list = array(\n    'example.com',\n    'example.org',\n);\n\n// Optional — logged-in users that bypass the check entirely.\n$domainlimit_exempt_users = array( 'admin' );\n\n// Optional — override the rejection message (a generic default is used otherwise).\n$domain_allowlist_message = 'Only links from approved domains can be shortened here.';\n\n// Optional — Telegram alerts about rejected domains. Omit the whole block to\n// disable alerting (the domain restriction still works).\n$domain_allowlist_alerts = array(\n    'telegram_token'      =\u003e '123456:ABC-DEF...', // @BotFather token\n    'telegram_chat'       =\u003e '-1001234567890',    // chat / group / channel id\n    'mode'                =\u003e 'both',              // instant | digest | both | off\n    'per_domain_cooldown' =\u003e 21600,               // re-alert same domain after N s (default 6h)\n    'global_min_gap'      =\u003e 30,                   // min seconds between any two sends\n    'digest_interval'     =\u003e 86400,               // digest cadence in seconds (default 24h)\n    'max_hosts'           =\u003e 200,                  // cap on stored distinct domains\n    'store_path'          =\u003e '',                   // '' = auto (plugin data/ -\u003e system temp)\n);\n```\n\n## Config file vs admin page\n\nBoth work, and **`config.php` takes precedence**:\n\n- **Allowlist** — `$domainlimit_list` in `config.php` is the read-only base; the\n  admin page manages an *additional* list stored in the database, and the\n  effective allowlist is the **union** of the two. \"Add to allowlist\" on a\n  rejected domain appends to that admin list. (A plugin can't write `config.php`,\n  so GUI-added domains live in the DB.)\n- **Alerts \u0026 message** — if `$domain_allowlist_alerts` / `$domain_allowlist_message`\n  are set in `config.php`, they win and the admin fields are read-only; otherwise\n  they're editable on the admin page and stored in the database.\n\n## How storage and the digest work (no DB, no cron)\n\n- **Storage** is one JSON file holding a map of `host -\u003e {hits, first_seen,\n  last_seen, last_notified}` plus two timestamps. It is updated under an\n  exclusive file lock (`flock`); the network send happens *after* the lock is\n  released, so concurrent shorten requests never wait on Telegram.\n- The map is **hard-capped** at `max_hosts` entries (least-recently-seen are\n  evicted), so the file size is bounded — there is no risk of filling the disk\n  the way an append-only log would.\n- The file lives in the plugin's `data/` directory if it is writable, otherwise\n  in the system temp directory, or wherever `store_path` points.\n- The **digest is traffic-driven**: every rejection checks whether\n  `digest_interval` has elapsed since the last digest and, if so, sends one.\n  No real cron is needed — ordinary site traffic does the ticking.\n\n## Scale and caveats\n\n- Tuned for **small/medium shorteners**. On very high traffic prefer\n  `mode =\u003e 'digest'` with a larger `digest_interval` to keep Telegram quiet.\n- Because the digest is traffic-driven, a site with no traffic sends nothing\n  (there is nothing to report anyway).\n- State is **per-server**: on a multi-server / load-balanced setup, throttling\n  and the digest are tracked independently on each node.\n- Alerts need a **writable** storage location. If none is writable the plugin\n  disables alerting and shows an admin notice (it will not send unthrottled).\n\n## License\n\nGPL-2.0-or-later. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseventhsite%2Fyourls-domain-allowlist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseventhsite%2Fyourls-domain-allowlist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseventhsite%2Fyourls-domain-allowlist/lists"}