{"id":22270114,"url":"https://github.com/ipshipyard/p2p-forge","last_synced_at":"2025-07-28T13:31:44.385Z","repository":{"id":256660200,"uuid":"843584470","full_name":"ipshipyard/p2p-forge","owner":"ipshipyard","description":"An Authoritative DNS server for distributing DNS subdomains to libp2p peers","archived":false,"fork":false,"pushed_at":"2024-11-27T18:12:46.000Z","size":212,"stargazers_count":5,"open_issues_count":4,"forks_count":0,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-12-03T09:13:20.259Z","etag":null,"topics":["autotls","dns-01-acme-challenge","libp2p","libp2p-transport","tls","wss"],"latest_commit_sha":null,"homepage":"https://registration.libp2p.direct/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ipshipyard.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2024-08-16T21:01:13.000Z","updated_at":"2024-11-27T20:49:26.000Z","dependencies_parsed_at":"2024-09-16T17:13:42.241Z","dependency_job_id":"d86f6a38-1619-42bc-ad61-a65971c0ce44","html_url":"https://github.com/ipshipyard/p2p-forge","commit_stats":null,"previous_names":["ipshipyard/p2p-forge"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipshipyard%2Fp2p-forge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipshipyard%2Fp2p-forge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipshipyard%2Fp2p-forge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipshipyard%2Fp2p-forge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ipshipyard","download_url":"https://codeload.github.com/ipshipyard/p2p-forge/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227914258,"owners_count":17839245,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["autotls","dns-01-acme-challenge","libp2p","libp2p-transport","tls","wss"],"created_at":"2024-12-03T12:07:22.627Z","updated_at":"2025-07-28T13:31:44.378Z","avatar_url":"https://github.com/ipshipyard.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# p2p-forge\n\n\n\u003e An Authoritative DNS server and HTTP+libp2p API for distributing DNS subdomains with CA-signed TLS certificates to libp2p peers.\n\n\u003ca href=\"http://ipshipyard.com/\"\u003e\u003cimg align=\"right\" src=\"https://github.com/user-attachments/assets/39ed3504-bb71-47f6-9bf8-cb9a1698f272\" /\u003e\u003c/a\u003e\nThis repository includes golang client and the backend of AutoTLS feature (see [blog.libp2p.io/autotls](https://blog.libp2p.io/autotls/) and [`AutoTLS` in Kubo](https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls)).  \nA public utility instance for libp2p at `libp2p.direct` is maintained by [Interplanetary Shipyard](https://github.com/ipshipyard).\n\n- [High-level Design](#high-level-design)\n  - [Peer Authentication and DNS-01 Challenge and Certificate Issuance](#peer-authentication-and-dns-01-challenge-and-certificate-issuance)\n  - [DNS Resolution and TLS Connection](#dns-resolution-and-tls-connection)\n- [Build](#build)\n- [Install](#install)\n  - [From source](#from-source)\n- [Usage](#usage)\n  - [Local testing](#local-testing)\n  - [Docker](#docker)\n  - [Configuration](#configuration)\n    - [ipparser Syntax](#ipparser-syntax)\n    - [acme Syntax](#acme-syntax)\n  - [Example](#example)\n  - [Handled DNS records](#handled-dns-records)\n    - [IPv4 subdomain handling](#ipv4-subdomain-handling)\n    - [IPv6 subdomain handling](#ipv6-subdomain-handling)\n  - [Submitting Challenge Records](#submitting-challenge-records)\n  - [Health Check](#health-check)\n\n## High-level Design\n\nThe following diagrams show the high-level design of how p2p-forge works.\n\n### Peer Authentication and DNS-01 Challenge and Certificate Issuance\n\n```mermaid\nsequenceDiagram\n    participant Client as Kubo (libp2p peer)\n    participant LE as Let's Encrypt (ACME Server)\n    participant Registration as registration.libp2p.direct (p2p-forge/acme)\n    participant DNS as libp2p.direct DNS (p2p-forge/acme)\n\n    Client-\u003e\u003eLE: Request Certificate\n    LE--\u003e\u003eClient: Respond with DNS-01 Challenge\n\n    Client-\u003e\u003eRegistration: Authenticate as PeerID over HTTP and share Multiaddrs and DNS-01 value\n    Registration-\u003e\u003eClient: Test public reachability of PeerID and Multiaddrs\n\n    Registration-\u003e\u003eDNS: Add Domain Validation DNS-01 TXT Record for \u003cPeerID\u003e.libp2p.direct\n    DNS--\u003e\u003eClient: DNS-01 TXT Record Added at _acme-challenge.\u003cPeerID\u003e.libp2p.direct\n\n    Client-\u003e\u003eLE: Notify DNS-01 Challenge Completion\n    LE-\u003e\u003eDNS: Validate DNS-01 Challenge\n    DNS--\u003e\u003eLE: Return TXT Record from _acme-challenge.\u003cPeerID\u003e.libp2p.direct\n\n    LE--\u003e\u003eClient: Certificate for *.\u003cPeerID\u003e.libp2p.direct issued\n```\n\n- DNS TXT record at `_acme-challenge.\u003cpeerid\u003e.libp2p.direct` is part of [ACME DNS-01 Challenge](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge)\n- HTTP API at `/v1/_acme-challenge`  is provided by [p2p-forge/acme](https://github.com/ipshipyard/p2p-forge/tree/main/acme) and requires [libp2p node and a valid PeerID](https://docs.libp2p.io/concepts/fundamentals/peers/) to pass PeerID auth and libp2p connectivity challenge.\n- Golang client for this entire flow is provided in [p2p-forge/client](https://github.com/ipshipyard/p2p-forge/tree/main/client)\n\n### DNS Resolution and TLS Connection\n\n```mermaid\nsequenceDiagram\n    participant Browser as Client (Web Browser)\n    participant DNS as libp2p.direct DNS NS (p2p-forge/ipparser)\n    participant Kubo as Kubo (IP: 1.2.3.4)\n\n    Browser--\u003e\u003eDNS: DNS Query: 1-2-3-4.\u003cPeerID\u003e.libp2p.direct\n    DNS--\u003e\u003eBrowser: 1.2.3.4\n\n    Browser-\u003e\u003eKubo: TLS Connect to 1.2.3.4 with SNI 1-2-3-4.\u003cPeerID\u003e.libp2p.direct\n```\n\n- DNS A/AAA responses for `*.\u003cPeerID\u003e.libp2p.direct` are  handled by [p2p-forge/ipparser](https://github.com/ipshipyard/p2p-forge/tree/main/ipparser)\n- TLS with [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication) is how web browsers establish [libp2p WebSockets transport](https://github.com/libp2p/specs/blob/master/websockets/README.md) connection\n\n## Build\n\n`go build` will build the `p2p-forge` binary in your local directory\n\n## Install\n\n```console\n$ go install github.com/ipshipyard/p2p-forge@latest\n```\n\nWill download using go mod, build and install the binary in your global Go binary directory (e.g. `~/go/bin`)\n\n### From source\n\n`go install` will build and install the `p2p-forge` binary in your global Go binary directory (e.g. `~/go/bin`)\n\n## Usage\n\n### Local testing\n\nBuild and run a custom Corefile configuration and on custom ports (DNS port set to `5354` via CLI, HTTP port set to `5380` via custom Corefile):\n\n```console\n$ ./p2p-forge -conf Corefile.local-dev -dns.port 5354\n```\n\nTest with `dig`:\n\n```console\n$ dig A 1-2-3-4.k51qzi5uqu5dlwfht6wwy7lp4z35bgytksvp5sg53fdhcocmirjepowgifkxqd.libp2p.direct @localhost -p 5354\n1.2.3.4\n\n$ curl http://localhost:5380/v1/health -I\nHTTP/1.1 204 No Content\n```\n\nTo run on port `53` as non-root user, adjust permission:\n\n```console\n$ sudo setcap cap_net_bind_service=+ep /path/to/p2p-forge\n```\n\n### Docker\n\nPrebuilt images for `main` and `staging` branches are provided at https://github.com/ipshipyard/p2p-forge/pkgs/container/p2p-forge\n\nDocker image ships without `/p2p-forge/Corefile` and `/p2p-forge/zones`, and you need to pass your own:\n\n```console\n$ docker build -t p2p-forge . \u0026\u0026 docker run --rm -it --net=host -v ./Corefile:/p2p-forge/Corefile.example -v ./zones:/p2p-forge/zones p2p-forge -conf /p2p-forge/Corefile.example -dns.port 5353\n```\n\nTest with `dig`:\n\n```console\n$ dig A 1-2-3-4.k51qzi5uqu5dlwfht6wwy7lp4z35bgytksvp5sg53fdhcocmirjepowgifkxqd.libp2p.direct @localhost -p 5353\n1.2.3.4\n```\n\n### Configuration\n\nThis binary is based on [CoreDNS](https://github.com/coredns/coredns) which is itself based on Caddy.\nTo run the binary create a file `Corefile` following the syntax listed in the CoreDNS documentation.\n\nA custom configuration can be passed via `./p2p-forge -conf Corefile.example`\n\nThis binary introduces two additional plugins:\n- `ipparser` which handles returning A and AAAA records for domains like `\u003cencoded-ip-address\u003e.\u003cpeerID\u003e.libp2p.direct`\n- `acme` which handles reading and writing DNS acme challenges for domains like `_acme-challenge.\u003cpeerID\u003e.libp2p.direct`\n\n#### ipparser Syntax\n\n~~~\nipparser FORGE_DOMAIN\n~~~\n\n**FORGE_DOMAIN** the domain of the forge (e.g. libp2p.direct)\n\n#### acme Syntax\n\n~~~\nacme FORGE_DOMAIN {\n\t[registration-domain REGISTRATION_DOMAIN [listen-address=ADDRESS] [external-tls=true|false]\n\t[database-type DB_TYPE [...DB_ARGS]]\n}\n~~~\n\n- **FORGE_DOMAIN** the domain suffix of the forge (e.g. `libp2p.direct`)\n- **REGISTRATION_DOMAIN** the HTTP API domain used by clients to send requests for setting ACME challenges (e.g. `registration.libp2p.direct`)\n   - **ADDRESS** is the address and port for the internal HTTP server to listen on (e.g. :1234), defaults to `:443`.\n   - `external-tls` should be set to `true` if the TLS termination (and validation of the registration domain name) will happen externally or should be handled locally, defaults to false\n- **DB_TYPE** is the type of the backing database used for storing the ACME challenges. Options include:\n  - `dynamo TABLE_NAME` for production-grade key-value store shared across multiple instances (where all credentials are set via AWS' standard environment variables: `AWS_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`)\n  - `badger DB_PATH` for local key-value store (good for local development and testing)\n\n### Example\n\nBelow is a basic example of starting a DNS server that handles the IP based domain names as well as ACME challenges.\nIt does the following:\n- Handles IP-based names and ACME challenges for the libp2p.direct forge\n- Sets up a standard HTTPS listener for registration.libp2p.direct to handle setting ACME challenges\n- Uses dynamo as a backend for ACME challenges\n\n``` corefile\n. {\n    log\n    ipparser libp2p.direct\n    acme libp2p.direct {\n        registration-domain registration.libp2p.direct listen-address=:443 external-tls=false\n        database-type dynamo mytable\n    }\n}\n```\n\n### Handled DNS records\n\nThere are 3 types of records handled for a given peer and forge (e.g. `\u003cpeerID\u003e.libp2p.direct`):\n- ACME Challenges for a given peerID `_acme-challenge.\u003cpeerID\u003e.libp2p.direct`\n- A records for an IPv4 prefixed subdomain like `1-2-3-4.\u003cpeerID\u003e.libp2p.direct`\n- AAAA records for an IPv6 prefixed subdomain like `2001-db8--.\u003cpeerID\u003e.libp2p.direct`\n\n#### IPv4 subdomain handling\n\nIPv4 handling is fairly straightforward, for a given IPv4 address `1.2.3.4` convert the `.`s into `-`s and the result\nwill be valid.\n\n#### IPv6 subdomain handling\n\nDue to the length of IPv6 addresses there are a number of different formats for describing IPv6 addresses.\n\nThe addresses handled here are:\n- For an address `A:B:C:D:1:2:3:4` convert the `:`s into `-`s and the result will be valid.\n- Addresses of the form `A::C:D` can be converted either into their expanded form or into a condensed form by replacing\nthe `:`s with `-`s, like `A--C-D`\n- When there is a `:` as the first or last character it must be converted to a 0 to comply with [rfc1123](https://datatracker.ietf.org/doc/html/rfc1123#section-2)\n, so `::B:C:D` would become `0--B-C-D` and `1::` would become `1--0`\n\nOther address formats (e.g. the dual IPv6/IPv4 format) are not supported\n\n### Submitting Challenge Records\n\nTo claim a domain name like `\u003cpeerID\u003e.libp2p.direct` requires:\n1. The private key corresponding to the given peerID\n2. A publicly reachable libp2p endpoint with \n   - one of the following libp2p transport configurations:\n     - QUIC-v1\n     - TCP or WS or WSS, Yamux, TLS or Noise\n     - WebTransport\n     - Other transports are under consideration (e.g. HTTP), if they are of interest please file an issue\n   - the [Identify protocol](https://github.com/libp2p/specs/tree/master/identify) (`/ipfs/id/1.0.0`)\n\nTo set an ACME challenge send an HTTP request to the server (for libp2p.direct this is registration.libp2p.direct)\n```shell\ncurl -X POST \"https://registration.libp2p.direct/v1/_acme-challenge\" \\\n-H \"Authorization: libp2p-PeerID bearer=\\\"\u003cbase64-encoded-opaque-blob\u003e\\\"\"\n-H \"Content-Type: application/json\" \\\n-d '{\n  \"Value\": \"your_acme_challenge_token\",\n  \"Addresses\": [\"your\", \"multiaddrs\"]\n}'\n```\n\nWhere the bearer token is derived via the [libp2p HTTP PeerID Auth Specification](https://github.com/libp2p/specs/blob/master/http/peer-id-auth.md).\n\n### Health Check\n\n`/v1/health` will always respond with HTTP 204\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fipshipyard%2Fp2p-forge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fipshipyard%2Fp2p-forge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fipshipyard%2Fp2p-forge/lists"}