{"id":51902552,"url":"https://github.com/txemi/darnlink","last_synced_at":"2026-07-26T18:00:14.645Z","repository":{"id":371916423,"uuid":"1297480499","full_name":"txemi/darnlink","owner":"txemi","description":"Auto-healing Markdown links — anchor links to a UUID so they survive refactors: repair moved links, robustify plain ones. No database, editor-agnostic.","archived":false,"fork":false,"pushed_at":"2026-07-17T19:01:54.000Z","size":470,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-17T19:22:05.378Z","etag":null,"topics":["cli","documentation","links","markdown","markdown-links","pre-commit","python","self-healing","uuid"],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/txemi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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-07-11T13:47:00.000Z","updated_at":"2026-07-17T19:01:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/txemi/darnlink","commit_stats":null,"previous_names":["txemi/darnlink"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/txemi/darnlink","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txemi%2Fdarnlink","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txemi%2Fdarnlink/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txemi%2Fdarnlink/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txemi%2Fdarnlink/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/txemi","download_url":"https://codeload.github.com/txemi/darnlink/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txemi%2Fdarnlink/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35922387,"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-26T02:00:06.503Z","response_time":89,"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":["cli","documentation","links","markdown","markdown-links","pre-commit","python","self-healing","uuid"],"created_at":"2026-07-26T18:00:13.917Z","updated_at":"2026-07-26T18:00:14.638Z","avatar_url":"https://github.com/txemi.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# darnlink\n\n\u003e **Never break a Markdown link again.**\n\u003e Deterministic, automatic, self-healing links.\n\n[![PyPI](https://img.shields.io/pypi/v/darnlink.svg)](https://pypi.org/project/darnlink/)\n[![CI](https://github.com/txemi/darnlink/actions/workflows/ci.yml/badge.svg)](https://github.com/txemi/darnlink/actions/workflows/ci.yml)\n[![License: GPL v3](https://img.shields.io/badge/license-GPLv3-blue.svg)](LICENSE)\n[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)\n\nMarkdown is excellent for documentation: it gives you fine-grained version history of how a\ndocument evolves, and it is easy for both humans and language models to read and process. It has\none flaw — **links break the moment you refactor.** Move a folder, rename a file, and every link\npointing into it dies.\n\n`darnlink` fixes that flaw **deterministically and automatically**. Run it after (or before) any\nreorganisation and it heals the links. It is built for trees of **many nested Markdown files**\nthat get relocated and refactored over time.\n\n\u003e ### 🌐 Cross-repo web links (experimental, opt-in)\n\u003e\n\u003e Beyond local links, darnlink can also anchor and verify **cross-repo web links** — a Markdown link\n\u003e to a `https://github.com/owner/repo/blob/…` file in *another* repository, anchored to that file's\n\u003e `uuid`. The opt-in **`web-check --online`** command (off by default; tokenless for public\n\u003e destinations) anchors a plain link and verifies an anchored one, failing on drift. It's\n\u003e **experimental**, and the core stays fully offline unless you invoke it. See\n\u003e [Elevating your link gate §8](docs/elevating-your-link-gate.md) \u003c!-- uuid: e95eaed1-9866-4c48-a0d7-99a6382f5bf9 --\u003e.\n\n## See it heal a link\n\n![darnlink repairs a link whose target was moved — same uuid, new path](demo/demo.gif)\n\nA robust link carries its target's uuid inline and stays a normal, clickable Markdown link. Move the\ntarget and the path goes stale; `darnlink . --write` finds it by uuid and rewrites **only** the path:\n\n```diff\n-See the [design doc](docs/design.md) \u003c!-- uuid: 7f3a1e2c --\u003e\n+See the [design doc](architecture/design.md) \u003c!-- uuid: 7f3a1e2c --\u003e\n```\n\nThat's the whole idea: the link survived the move on its own.\n\n## Use it in one line — no install, no clone\n\ndarnlink is on [PyPI](https://pypi.org/project/darnlink/), so [`uv`](https://docs.astral.sh/uv/)\nfetches and runs it for you — nothing to install:\n\n```bash\n# dry-run: show what it would do (writes nothing)\nuvx darnlink \u003cfolder\u003e\n\n# apply\nuvx darnlink \u003cfolder\u003e --write\n```\n\nPrefer a permanent install? `pipx install darnlink` (or `uv tool install darnlink`), then just\n`darnlink \u003cfolder\u003e`.\n\n**Straight from the repo** — when you need a ref PyPI can't give you: an *immutable commit* for a\nreproducible CI gate (a tag can be force-moved; a SHA can't), or unreleased `main`:\n\n```bash\nuvx --from git+https://github.com/txemi/darnlink@v0.7.0 darnlink \u003cfolder\u003e   # a tag\nuvx --from git+https://github.com/txemi/darnlink@\u003csha\u003e  darnlink \u003cfolder\u003e   # an immutable commit\nuvx --from git+https://github.com/txemi/darnlink        darnlink \u003cfolder\u003e   # latest main\n```\n\n**Safe by default:** without `--write`, darnlink only *reports* what it would change — it never\nmodifies a file.\n\nUpgrade plain links so they self-heal in the future (and create a UUID where the target lacks one):\n\n```bash\nuvx darnlink \u003cfolder\u003e --robustify --create-frontmatter --write\n```\n\n## How it works (it's simple)\n\nPoint it at a documentation folder; it scans the links and does two things:\n\n- **Correct links → protected for the future.** It writes a `uuid` into the **target's**\n  frontmatter, and adds an **invisible HTML comment** carrying that same uuid next to the link at\n  the **source**. The link is now anchored to the file's *identity*, not to its path.\n- **Already-protected links that broke** (the target was moved or renamed) → **repaired**: darnlink\n  finds the target by its uuid and rewrites the path.\n\nEvery pass protects the correct links and repairs the broken-but-protected ones. It is\n**deterministic** (exact UUID match — no heuristics, no network), **idempotent**, and needs **no\ndatabase and no index file**: a repo that uses darnlink links still works with darnlink uninstalled.\n\nA robust link stays a normal, clickable Markdown link:\n\n```markdown\nSee [the design doc](docs/design.md) \u003c!-- uuid: 7f3a1e2c-... --\u003e\n```\n\nLinks to **directories** work too: a link to `docs/guide/` is anchored to the uuid of that folder's\n`README.md`, so it heals when the folder moves — same guarantee, for the hubs you link to by folder.\nIf a linked folder has no `README.md`, `--create-readme` makes one (with a uuid) so the link can be\nanchored; it is opt-in and only ever creates a README inside a directory that already exists.\n\nFormat spec: [FORMAT.md](FORMAT.md) \u003c!-- uuid: 9052d864-2a45-4ed4-8725-d8a394e7a7ef --\u003e.\n\n## Excluding parts of the tree\n\ndarnlink walks the **whole** folder by default. Skip the parts that must not be touched — vendored\nor submodule content, mirrors, generated output:\n\n```bash\ndarnlink \u003cfolder\u003e --exclude vendor --exclude mirror --ignore-block autogrid\n```\n\n`--exclude` and `--ignore-block` are repeatable. `--exclude` is a glob — **keep patterns tight**: a\nwide one like `*` silently drops directories from the scan (their links stop being checked). Prefer\nword-boundary patterns (`old`, `old_*`, `*_old`) over a greedy `*old*`. For a whole file rather than a\ndirectory or a region, a file can opt itself out from the inside — see [FORMAT.md §5](FORMAT.md#5-opting-a-file-out) \u003c!-- uuid: 9052d864-2a45-4ed4-8725-d8a394e7a7ef --\u003e.\nThe full flag list is under [All options](#all-options) below.\n\n## All options\n\nThe sections above introduce these in context; this is the full list (same as `darnlink --help`).\n\n| Option | What it does |\n|---|---|\n| `path` *(positional)* | Root directory to scan. Default: `.` — darnlink takes a **directory**, not a file list. (To write to specific files while still scanning the whole tree, use `--only`, below.) |\n| `--write` | Apply the changes. Without it darnlink only **reports** — it never modifies a file. |\n| `--robustify` | Upgrade plain links to robust. Without it the operation is *repair* (fix robust links whose target moved). |\n| `--create-frontmatter` | *(robustify)* Allow creating frontmatter on a target that has none, so it can take a `uuid`. Opt-in on purpose. |\n| `--no-create-frontmatter-for GLOB` | *(robustify)* Basename glob whose targets **never** get a `uuid` — no block created, no line inserted — regardless of `--create-frontmatter`. Reusing a `uuid` the target already has is unaffected. Repeatable. |\n| `--only FILE` | Restrict **writes** to these `.md` files. The tree is still scanned and indexed in full — so a link's target can live anywhere — but only these files are modified. Repeatable. See [Scoping writes to specific files](#scoping-writes-to-specific-files---only). |\n| `--only-from FILE` | Read `--only` paths from `FILE`, one per line (`-` = stdin). Combines with `--only`. Lets you pipe a generated list (e.g. staged files) without darnlink knowing about git. |\n| `--no-target-writes` | *(with `--only`)* Never write a `uuid` into a target **outside** the write scope: such links are left plain and reported. Guarantees **no** file outside `--only` is touched. |\n| `--exclude PATTERN` | Skip any directory whose name matches `PATTERN` (glob / `fnmatch`, case-sensitive; a plain name matches exactly). Repeatable — e.g. `--exclude old --exclude 'old_*' --exclude '*_old'` skips the whole `old` family. |\n| `--ignore-block NAME` | Leave links inside `\u003c!-- NAME-start --\u003e … \u003c!-- NAME-end --\u003e` blocks alone. Repeatable. |\n| `--json` | Machine-readable output (see below). |\n\nFiles can also opt themselves out from the inside, with no CLI flag: see\n[FORMAT.md §5](FORMAT.md#5-opting-a-file-out) \u003c!-- uuid: 9052d864-2a45-4ed4-8725-d8a394e7a7ef --\u003e for `\u003c!-- darnlink-ignore-links --\u003e` (leave *my* links\nalone, but keep anchoring to me) and `\u003c!-- darnlink-ignore-file --\u003e` (drop me from the graph).\n\n### `--json` output\n\nStable shape, meant for gates and scripts:\n\n```json\n{\n  \"wrote\": 0,\n  \"applied\": false,\n  \"write_scope\": null,\n  \"suppressed_outside_write_scope\": 0,\n  \"ignored_files\": [\"path/to/opted-out.md\"],\n  \"link_ignored_files\": [\"path/to/generated/INDEX.md\"],\n  \"invalid_frontmatter_files\": [],\n  \"findings\": [{ \"kind\": \"robustify\", \"file\": \"docs/a.md\", \"detail\": \"b.md +uuid \u003cuuid\u003e\" }]\n}\n```\n\n`kind` is one of: `repair`, `conflict`, `robustify`, `unresolvable`, `ambiguous`, `no_frontmatter`,\n`out_of_scope`, `deny_listed`, `ignored_links`, `invalid_frontmatter`, `target_uuid_write`,\n`target_write_refused`. A gate that wants \"is anything left to do?\" should count the kinds it cares\nabout (e.g. `robustify`) rather than the length of `findings` — the non-actionable kinds are\nreported precisely so nothing is skipped silently.\n\n`write_scope` is `null` unless `--only` is in effect, when it lists the files that may be written;\n`suppressed_outside_write_scope` counts actionable findings in files outside that scope — not shown,\nbut surfaced so a narrowed run never reads as a clean tree.\n\n`out_of_scope` is worth calling out: it means a plain link's target **exists but was never scanned**\n(it lives outside `path`, or an `--exclude` skipped it), so its `uuid` is unknown and the link is\nleft plain. That is a different fact from `no_frontmatter` (target scanned, but it has none) — the\ntwo used to collapse into one, which reported a scope miss as if the target were malformed.\n\n### Scoping writes to specific files (`--only`)\n\nBy default darnlink writes to every file it scans. `--only` narrows the **write** scope to named\nfiles while still building the target graph from the whole tree — the two are separate axes:\n\n```bash\n# Scan the whole repo (so targets resolve wherever they live), but only rewrite ONE file:\ndarnlink . --robustify --write --only tasks/56/README.md\n```\n\nWhy the split matters: the link you want to anchor lives in your file, but its **target** usually\nlives elsewhere in the repo. Scanning only your subtree (`darnlink tasks/56`) can't see the target,\nso it reports `out_of_scope` and anchors nothing; scanning the whole repo without `--only` would\nrewrite every robustifiable link in the tree, including other people's work in flight. `--only` is\n\"read everything, write here\".\n\n- The **one** write that may land outside `--only` is adding a `uuid` to a *target* so the link can\n  be anchored at all — it is reported (kind `target_uuid_write`) before it happens. Pass\n  `--no-target-writes` to refuse even that: the link stays plain, and **nothing** outside `--only`\n  is touched.\n- A **repair** run under `--only` only ever inspects the links *inside* the scoped files. A moved\n  target's *inbound* links live in files you didn't name — a clean scoped result is **not** proof the\n  tree is clean. Keep a full-tree run in CI for that.\n- darnlink never touches git. To scope to your staged files, pipe them in — this is the whole\n  `--only-from -` use case:\n\n```bash\ngit diff --cached --diff-filter=ACMR --name-only -- '*.md' \\\n  | darnlink . --robustify --write --only-from -\n```\n\n  (darnlink edits the files but does **not** re-stage them; your hook re-runs `git add` if it wants\n  the fixes in the same commit.)\n\n## For language models / agents\n\nIf you are managing a repo of nested Markdown docs and you move folders around, the links break.\nBefore or after a refactor, run the one-liner above on the affected folder — **dry-run first**, then\n`--write` to apply. You do **not** need to clone darnlink or read its code; the command fetches and\nruns it for you.\n\n## Never break a link again — add it to your quality gate\n\ndarnlink **exits non-zero** when a robust link is broken, so any gate that runs it will block the\nbreakage before it lands. Pick the one that fits your workflow — near copy-paste:\n\n\u003e **Want a ready-made wrapper instead of wiring it yourself?** [`recipes/darnlink-gate`](recipes/README.md) \u003c!-- uuid: b4e6058b-4af0-4d23-a826-975a8fc78e6f --\u003e\n\u003e does all of the below (both checks, staged-in-pre-commit vs whole-repo-in-CI, pinned ref, fail-open)\n\u003e from a tiny `darnlink-gate.json`. It's a reference recipe, fetchable in CI without a token — with\n\u003e complete copy-paste hook \u0026 CI files in [`recipes/examples/`](recipes/examples/) \u003c!-- uuid: f8da8344-8293-4c05-b154-8bdb088adddf --\u003e.\n\n**1. pre-commit** (recommended — darnlink ships a hook):\n\n```yaml\n# .pre-commit-config.yaml\nrepos:\n  - repo: https://github.com/txemi/darnlink\n    rev: v0.7.0\n    hooks:\n      - id: darnlink            # fail the commit if any robust link is broken\n      # - id: darnlink-repair   # …or auto-repair in place instead of failing\n```\n\n**2. GitHub Actions** (gate every push \u0026 PR):\n\n```yaml\n# .github/workflows/darnlink.yml\nname: darnlink\non: [push, pull_request]\njobs:\n  links:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: astral-sh/setup-uv@v5\n      - run: uvx darnlink .\n```\n\n**3. Plain git hook** (no framework):\n\n```bash\n# .git/hooks/pre-commit  (chmod +x)\n#!/usr/bin/env bash\nuvx darnlink . || {\n  echo \"darnlink: broken robust links — re-run with --write to repair\"; exit 1\n}\n```\n\n\u003e The three run the same check (`darnlink \u003cfolder\u003e`, dry-run): they **report** breakage and fail.\n\u003e To have the gate **fix** links instead of just failing, use `--write` (Actions/hook) or the\n\u003e `darnlink-repair` hook id (pre-commit).\n\n### Stricter: require every link to be *robust* (fail-closed)\n\nThe gate above keeps the robust links you already have from breaking. It says nothing about **plain**\nrelative links that were never anchored — so a fresh, un-anchored link sails through, and the next\nrefactor silently breaks it. To close that gap, run the **robustify check** (dry-run — it reports,\nit does **not** write):\n\n```bash\ndarnlink . --robustify        # exits non-zero if any plain link to an anchorable target is un-anchored\n```\n\n\u003e **One command for both axes — `darnlink check`.** `--robustify` and plain `darnlink .` catch\n\u003e *disjoint* failures (an un-anchored plain link vs. a broken robust link) — a gate that runs only one\n\u003e is blind to the other. `darnlink check` runs **both** in one report-only invocation and exits with a\n\u003e distinguishable code — `0` clean · `2` integrity (broken/invalid) · `3` strict (un-anchored) — so CI\n\u003e can't forget a half and can tell *which* axis failed. darnlink checks; your CI/hook decides to block.\n\nThis is **fail-closed**: it fails until every link that *can* be robust *is* robust. A target is\n*anchorable* when it's a local Markdown file with frontmatter (darnlink reuses its `uuid`, or adds\none). Links whose target **can't** take a `uuid` are left alone — external/non-local targets,\ndeny-listed targets, and targets **without frontmatter** (unless you opt in with\n`--create-frontmatter`). So it only demands robustness where robustness is possible. Wire it as a\npre-commit hook with the `darnlink-strict` id:\n\n```yaml\n# .pre-commit-config.yaml\nrepos:\n  - repo: https://github.com/txemi/darnlink\n    rev: v0.7.0   # darnlink-strict ships since v0.2.0\n    hooks:\n      - id: darnlink            # links that *are* robust must not break\n      - id: darnlink-strict     # …and every anchorable link *must* be robust (fail-closed)\n```\n\nTo adopt it on an existing repo: anchor what's already anchorable once with\n`darnlink . --robustify --write` (review the diff, commit), then the gate stays green.\n\n\u003e **📘 Going all the way — elevate a whole repo to fail-closed.** The strictest setting\n\u003e (`--robustify --create-frontmatter`: *every link's target must carry a `uuid`*) is reachable even\n\u003e on a large repo with a big generated mirror. The end-to-end playbook — the two-bucket strategy,\n\u003e how generators cooperate (stable `uuid` + the `darnlink-ignore-links` marker), bulk-adopting a mirror from\n\u003e its stored raw, the traps, and the pre-commit/pre-push/CI wall architecture — is in\n\u003e **[docs/elevating-your-link-gate.md](docs/elevating-your-link-gate.md) \u003c!-- uuid: e95eaed1-9866-4c48-a0d7-99a6382f5bf9 --\u003e**.\n\n\u003e **Scope note for repos with many contributors.** All the hooks run over the **whole tree**\n\u003e (`pass_filenames: false` — darnlink takes a directory, not a file list), and the strict check is\n\u003e *fail-closed*. So a plain, un-anchored link that **someone else** left in a file you never touched\n\u003e will block **your** commit. That is fine for a small repo, but with several people (or parallel\n\u003e agents) committing at once it means one un-anchored link blocks everyone. A practical split: run\n\u003e `darnlink-strict` in **CI** (the real wall — nothing un-anchored lands on the main branch), and the\n\u003e plain `darnlink` hook **locally** (fast, and it only fails on links that actually broke), so a\n\u003e teammate's in-flight plain link doesn't stop your commit.\n\n**Generated files** with plain links you don't want to anchor: have the generator emit\n`\u003c!-- darnlink-ignore-links --\u003e` (just below the frontmatter). darnlink then leaves the links inside\nthem alone — no churn when the generator re-runs — while they stay linkable targets, which matters\nbecause a generated `INDEX.md` is usually what everything else links *to*. Use\n`\u003c!-- darnlink-ignore-file --\u003e` only for a file that should leave the graph entirely (it also stops\nresolving inbound links), or `--exclude \u003cdir\u003e` for a whole tree. See\n[FORMAT.md §5](FORMAT.md#5-opting-a-file-out) \u003c!-- uuid: 9052d864-2a45-4ed4-8725-d8a394e7a7ef --\u003e\nfor the two markers side by side. darnlink itself is gated this way (see `tools/check.sh` / CI).\n\n## Used by\n\n- [immich-autotag](https://github.com/txemi/immich-autotag) — a rule engine for organizing Immich photo libraries — runs darnlink as a **read-only docs-link quality gate** in pre-commit, Jenkins, and GitHub Actions, so its Markdown docs links don't break when files move.\n\n## Prior art \u0026 how darnlink differs\n\nThe idea of surviving refactors by anchoring to an identity isn't new, but the specific combination is a gap:\n\n- **emacs `org-id`** — the closest relative: `[[id:UUID]]` links survive moving files. But it's **org, not Markdown**, the link is *only* the id, and resolution needs a **central database** (`~/.org-id-locations`) tied to emacs.\n- **Obsidian / VS Code / Front Matter CMS** — update links on rename, but **path-based** and only *inside the app*: a `git mv` or any external script breaks them. They depend on the editor.\n- **markdown-link-check / dead-link-checker** — only **detect** broken links; they neither repair nor use a uuid.\n- **Docusaurus / MkDocs / 11ty** — map ids→urls at *site build* time; not a repo-maintenance tool.\n\n**darnlink's niche:** Markdown-native, **no database**, **editor-agnostic**. The link carries the human path **and** the uuid inline — `[text](path) \u003c!-- uuid --\u003e` — so it stays clickable and readable even when \"broken\", and is self-describing (uuid by the link and in the target's frontmatter). And it both **repairs** moved paths *and* **upgrades** plain links to robust ones. *\"What `org-id` does for emacs, but for plain Markdown and with no database.\"*\n\n## Status\n\nEarly (v0.7.0). Built spec-first with [GitHub Spec Kit](https://github.com/github/spec-kit) — see\n`.specify/` and `specs/`.\n\n## License\n\ndarnlink is free software, licensed under the **GNU General Public License v3.0 or later**\n(GPL-3.0-or-later) — see [LICENSE](LICENSE). A copyleft license: derivative versions you distribute\nmust also be open source. (Invoking darnlink as a command on your repo does **not** affect your\nrepo's or your project's license — only modified versions of darnlink itself.)\n\nCopyright (C) 2026 txemi.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftxemi%2Fdarnlink","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftxemi%2Fdarnlink","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftxemi%2Fdarnlink/lists"}