diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 0000000..0d5ef57 --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,34 @@ +name: Documentation + +on: + pull_request: + paths: + - "README.md" + - "DOCKER.md" + - "docs/**" + - "content/images/demo/**" + - "scripts/check-docs.py" + - ".github/workflows/documentation.yml" + push: + branches: + - master + paths: + - "README.md" + - "DOCKER.md" + - "docs/**" + - "content/images/demo/**" + - "scripts/check-docs.py" + - ".github/workflows/documentation.yml" + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Validate documentation + run: python3 scripts/check-docs.py diff --git a/DOCKER.md b/DOCKER.md index 8c3c118..acff546 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -1,353 +1,17 @@ -# TruthGate Docker appliance - -TruthGate ships as one container containing the TruthGate ASP.NET application, -Kubo, and the matching `ipfs` CLI. Docker owns the software lifecycle; mounted -state survives image replacement. - -The appliance is intentionally configured as a contributing IPFS server. It is -not a quiet desktop-node preset: DHT server mode, inbound TCP/UDP swarm -transports, content providing, and automatic repository GC are enabled unless -an operator explicitly overrides them. - -## Requirements - -- Docker Engine with the Compose v2 plugin -- Docker Compose 2.24.4 or newer for the development override's `!override` - merge directive -- TCP ports 80, 443, and 4001 plus UDP port 4001 available by default -- Public TCP and UDP forwarding for the selected swarm port when deployed - behind NAT - -## Production quick start - -```bash -git clone https://github.com/magiccodingman/TruthGate-IPFS.git -cd TruthGate-IPFS -cp .env.example .env -docker compose pull -docker compose up -d -``` - -The default Compose image is `magiccodingman/truthgate-ipfs:stable`. Open -`https://localhost` after the container becomes healthy. The first connection -uses TruthGate's self-signed fallback certificate unless a configured domain -has an issued certificate. - -On the first boot, retrieve the generated administrator password with: - -```bash -docker compose logs truthgate -``` - -The username is `admin`. Change its password in the TruthGate UI. Until the -configuration is persisted, the bootstrap password is also retained at -`data/truthgate/state/bootstrap-admin-password` with restrictive permissions. - -To build the production image locally instead of pulling the stable release: - -```bash -docker compose up --build -d -``` - -## Published image tags - -A successful promotion to the protected `stable` branch runs the complete -AMD64/ARM64 appliance, legacy-repository migration, and TLS lifecycle gates -before Docker Hub publishing starts. - -The release workflow publishes: - -```text -magiccodingman/truthgate-ipfs:stable -magiccodingman/truthgate-ipfs:latest -magiccodingman/truthgate-ipfs:0.1 -magiccodingman/truthgate-ipfs:0.1.0 -magiccodingman/truthgate-ipfs:sha- -``` - -`stable` and `latest` move to the newest successful stable release. The -major/minor series tag, such as `0.1`, also moves forward. Full semantic -versions are immutable release identifiers. `VERSION` contains the intentional -major/minor series; every successful stable promotion automatically allocates -the next patch version. - -The same multi-platform tag resolves to the correct `linux/amd64` or -`linux/arm64` image automatically. Replacing the container does not replace -mounted state. - -To update an existing deployment: - -```bash -docker compose pull -docker compose up -d -``` - -Pin `TRUTHGATE_IMAGE` in `.env` to a full version when an installation should -not automatically follow the moving `stable` tag. - -## Persistent storage contract - -The default deployment keeps three host paths separate: - -```text -data/ -├── truthgate/ # config, database, certificates, secrets, app state -└── ipfs/ - ├── repo/ # Kubo identity, config, datastore, keystore, repo version - └── blocks/ # Kubo block files -``` - -They are mounted as: - -```text -/data/truthgate -/data/ipfs/repo -/data/ipfs/repo/blocks -``` - -Kubo still sees a conventional `$IPFS_PATH/blocks` directory, while the host -can later relocate only the blockstore by changing `IPFS_BLOCKS_HOST_PATH`. -No network filesystem, FUSE, JuiceFS, mount management, or mount health logic -is included in this implementation. - -The software inside the image is disposable. Backups should target the mounted -state, not the .NET runtime, Kubo binary, application binaries, or build output. - -## Opinionated Kubo server defaults - -A new repository is initialized with Kubo's `server` profile. An existing -repository receives that profile once, tracked by -`data/truthgate/state/kubo-server-profile-v1`. The profile disables mDNS and -automatic NAT port mapping and filters non-public address ranges, matching a -public server deployment. Set `TRUTHGATE_KUBO_APPLY_SERVER_PROFILE=false` -before the first managed boot when that behavior is not appropriate. - -TruthGate then manages these defaults: - -```text -Routing.Type dhtserver -Addresses.Swarm current Kubo defaults, with missing - TCP/QUIC/WebTransport listeners restored -Swarm.EnableHolePunching true -Swarm.RelayClient.Enabled true -Provide.Enabled true -Provide.Strategy all -Provide.DHT.Interval 22h -Provide.DHT.SweepEnabled true -Provide.DHT.ResumeEnabled true -Datastore.StorageMax auto: 90% of the blockstore filesystem -Datastore.StorageGCWatermark 90 -Automatic repository GC enabled -Addresses.API /ip4/127.0.0.1/tcp/5001 -Addresses.Gateway /ip4/127.0.0.1/tcp/9010 -``` - -The entrypoint preserves Kubo's current listener array and only appends missing -standard listeners. That avoids deleting newer transports such as -WebTransport when Kubo changes its defaults. - -The RPC API and HTTP gateway are always loopback-only. TruthGate already -authenticates and proxies public `/api/v0`, `/ipfs`, `/ipns`, and WebUI access. -Those two Kubo listener settings cannot be overridden. - -The public swarm mapping on port 4001 is the current implementation stage. -TruthGate is expected to own and proxy swarm transports in a future -architecture; until that transport layer exists, Docker publishes Kubo's TCP -and UDP swarm traffic directly. - -## Persistent Kubo settings - -On first boot TruthGate creates: - -```text -data/truthgate/config/kubo-settings.json -``` - -This file is persistent and contains the normal managed settings. It is also -the contract intended for future live management from the TruthGate UI. -Editing the file and restarting the container changes the effective Kubo -configuration without rebuilding the image. - -Environment variables take precedence over the persistent settings file. The -available variables are documented in `.env.example`, including: - -```text -TRUTHGATE_KUBO_ROUTING_TYPE -TRUTHGATE_KUBO_PROVIDE_ENABLED -TRUTHGATE_KUBO_PROVIDE_STRATEGY -TRUTHGATE_KUBO_STORAGE_MAX -TRUTHGATE_KUBO_STORAGE_PERCENT -TRUTHGATE_KUBO_ENABLE_GC -TRUTHGATE_KUBO_PUBLIC_IPV4 -TRUTHGATE_KUBO_PUBLIC_IPV6 -TRUTHGATE_KUBO_ANNOUNCE_PORT -``` - -For advanced settings, edit: - -```text -data/truthgate/config/kubo-overrides.json -``` - -It is a JSON object whose keys are Kubo config paths and whose values are raw -JSON values: - -```json -{ - "Swarm.ConnMgr.LowWater": 100, - "Swarm.ConnMgr.HighWater": 300, - "Routing.AcceleratedDHTClient": false -} -``` - -Advanced overrides run after TruthGate's normal defaults. `Addresses.API` and -`Addresses.Gateway` are ignored in this file because they are protected -loopback interfaces. - -## Storage policy - -The default storage policy is: - -```json -{ - "storage": { - "max": "auto", - "percent": 90, - "fallback": "200GB", - "gcWatermark": 90, - "enableGc": true - } -} -``` - -`auto` reads the total capacity of the filesystem containing -`/data/ipfs/repo/blocks` and sets Kubo's `Datastore.StorageMax` to 90 percent of -that total. It uses total capacity rather than currently free space so the -limit does not shrink as content is added. If capacity detection fails, the -fallback is `200GB`. - -A fixed value is also supported: - -```env -TRUTHGATE_KUBO_STORAGE_MAX=2TB -``` - -Auto mode recalculates on every container start, so expanding a VPS disk only -requires a container restart. Fixed mode remains exactly the configured value. -`StorageMax` is a soft Kubo blockstore/GC threshold rather than a hard quota, -so filesystem and metadata headroom still matter. - -## Public announce addresses - -By default TruthGate attempts to detect public IPv4 and IPv6 addresses and -manages matching TCP, QUIC-v1, and WebTransport entries in -`Addresses.AppendAnnounce`. Previously managed entries are removed before new -ones are added, so public-IP changes do not accumulate stale addresses. - -Each address can be automatic, disabled, or literal: - -```env -TRUTHGATE_KUBO_PUBLIC_IPV4=auto -TRUTHGATE_KUBO_PUBLIC_IPV6=off -TRUTHGATE_KUBO_ANNOUNCE_PORT=4001 -``` - -The announce port defaults to `IPFS_SWARM_PORT`. If Docker publishes -`14001:4001`, set `IPFS_SWARM_PORT=14001`; TruthGate will announce `14001` while -Kubo continues listening on container port 4001. - -## Kubo startup and migrations - -Every start uses automatic repository migration. Automatic GC is included when -enabled: - -```bash -ipfs daemon --migrate=true --enable-gc -``` - -The entrypoint applies managed settings before starting the daemon, then starts -TruthGate only after Kubo's loopback RPC API is ready. - -## Diagnostics - -Inspect the effective configuration and live node state with: - -```bash -docker exec truthgate truthgate-kubo-status -``` - -The report includes: - -- peer ID and connected peer count -- effective routing, provide, swarm, and storage configuration -- configured and active listen addresses -- public append-announcement addresses -- repository statistics -- AutoNAT observations -- sweep-provider statistics - -Useful direct checks include: - -```bash -docker exec truthgate ipfs config Routing.Type -docker exec truthgate ipfs config --json Addresses.Swarm -docker exec truthgate ipfs config --json Addresses.AppendAnnounce -docker exec truthgate ipfs provide stat -docker exec truthgate ipfs stats dht -``` - -A real external validation should use another IPFS node: - -```bash -ipfs routing findpeer -``` - -Then add a unique file through TruthGate, obtain its CID, and retrieve it from -the separate node. That verifies routing and content providing, not merely -local daemon health. - -## Development - -Development uses the same Dockerfile and base Compose service: - -```bash -docker compose -f compose.yaml -f compose.dev.yaml up --build -``` - -The override changes the image target to the .NET SDK development stage, -bind-mounts the repository at `/workspace`, adds persistent NuGet caches, and -runs the web project with `dotnet watch`. TruthGate is available at -`http://localhost:8080` by default. - -Kubo initialization, server defaults, repository migrations, persistent paths, -startup order, and process supervision are shared with production. This -prevents development from silently using a different node layout. - -Rider can use `compose.yaml` followed by `compose.dev.yaml` directly in a Docker -Compose run configuration. The source directory is mounted directly, so normal -edits trigger `dotnet watch`. - -## General configuration - -Ordinary host-facing settings live in `.env`. Important values include: - -- `TRUTHGATE_IMAGE` -- `KUBO_VERSION` -- `TRUTHGATE_HTTP_PORT` -- `TRUTHGATE_HTTPS_PORT` -- `IPFS_SWARM_PORT` -- `TRUTHGATE_DATA_HOST_PATH` -- `IPFS_REPO_HOST_PATH` -- `IPFS_BLOCKS_HOST_PATH` - -Use absolute host paths for advanced deployments. Relative defaults are rooted -at the directory containing `compose.yaml`. - -## Process model - -`tini` is PID 1 in normal Docker execution. Rider's debugger may become PID 1, -in which case Tini registers as a child subreaper. The entrypoint starts Kubo -first, waits for its RPC API, then starts TruthGate. If either process exits, -the entrypoint terminates the other and exits so Docker's restart policy can -recover the appliance. SIGTERM and SIGINT are forwarded to both processes for -graceful shutdown. +# TruthGate Docker documentation + +TruthGate is now documented through the versioned documentation tree. + +- [Docker installation](docs/setup/docker.md) +- [First run](docs/setup/first-run.md) +- [Configuration](docs/setup/configuration.md) +- [Networking](docs/setup/networking.md) +- [Storage and backups](docs/setup/storage-and-backups.md) +- [Updating and image tags](docs/setup/updating.md) +- [Legacy migration](docs/setup/migrating-from-legacy.md) +- [Developer workflow](docs/developer/index.md) +- [Environment-variable reference](docs/reference/environment-variables.md) +- [Ports and paths](docs/reference/ports-and-paths.md) +- [Kubo defaults](docs/reference/kubo-defaults.md) + +This compatibility page remains at the repository root so existing links to `DOCKER.md` continue to work. diff --git a/README.md b/README.md index ece6c62..2885aff 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,181 @@ # TruthGate -> The Secure, Self-Hosted Edge Gateway IPFS Always Needed, with Logins, API Keys, GUI Control, and Web3 Site Publishing. -> **[truthgate.io](https://truthgate.io)** for full docs, guides, and live demos. +**TruthGate is a self-hosted edge gateway, control plane, and static-site publishing system for Kubo/IPFS.** ---- +It packages the TruthGate web application, Kubo, and the matching `ipfs` CLI into one Docker appliance, then adds the operational layer that an internet-facing IPFS node usually needs: authenticated accounts, API keys, the native IPFS WebUI behind login, protected gateway and RPC routes, CID and IPNS pinning, domain routing, automatic TLS, static-site publishing, rate limiting, persistent storage, migrations, and diagnostics. -## Features at a Glance +[Read the documentation](docs/index.md) · [Docker Hub](https://hub.docker.com/r/magiccodingman/truthgate-ipfs) · [Report an issue](https://github.com/magiccodingman/TruthGate-IPFS/issues) -- **Secure User Logins** – Role-based accounts protect access. -- **API Key Management** – Scoped tokens for programmatic control. -- **Clean GUI** – Manage users, domains, and publishing in minutes. -- **`/ipfs/` + `/webui/`** – Native IPFS routes, now behind auth + HTTPS. -- **Auto SSL & Domains** – Custom domains with one-step HTTPS. -- **Drag-and-Drop Publishing** – Deploy WASM, SPAs, and Blazor apps in seconds. -- **Web2/Web3 Hybrid Hosting** – Serve both decentralized and traditional users. -- **Hardened Edge Gateway** – Secure IPFS exposure without the risks. +![TruthGate dashboard](content/images/demo/dashboard-dark.webp) ---- +## Why TruthGate exists -## Docker quick start +Kubo is the distributed content system. It addresses content, exchanges blocks, resolves IPNS, participates in the DHT, and exposes powerful local APIs. -TruthGate, Kubo, and the matching `ipfs` CLI run together as one appliance -container. Application state, Kubo repository metadata, and Kubo blocks are -persisted separately by default. +Running a useful public service around Kubo still leaves an operator with a different set of problems: + +- How do you administer the node without exposing Kubo's unrestricted RPC API to the internet? +- How do multiple people sign in with separate accounts? +- How do applications receive revocable API credentials? +- How do you use the familiar native IPFS WebUI remotely without opening it to everyone? +- How do you publish a static site, bind domains, route SPAs, and obtain certificates? +- How do you follow an IPNS name and keep its current CID pinned automatically? +- How do you update Kubo and TruthGate without losing the repository, identity, keys, database, certificates, or blocks? +- How do you protect public routes from obvious abuse while keeping real users and shared networks usable? + +TruthGate sits at that boundary. Kubo remains responsible for IPFS. TruthGate provides the authenticated management plane, publishing workflows, HTTP edge behavior, and operational controls around it. + +## What TruthGate does + +### Accounts, sessions, and API keys + +TruthGate has a login-protected management portal with role-based user accounts. The first boot creates an `admin` account with a unique generated password instead of a shared default credential. Additional users and password changes are managed from the portal. + +Applications can use API keys rather than interactive sessions. Keys are shown once when created and stored as hashes. They can be revoked without changing a user's password. + +![TruthGate user management](content/images/demo/users-dark.webp) + +### Native IPFS WebUI behind authentication + +TruthGate proxies the native IPFS WebUI through the authenticated portal. Operators keep the interface they already know for browsing files, creating folders, importing content, and managing pins, but the underlying Kubo gateway and RPC API stay bound to loopback inside the appliance. + +TruthGate also exposes familiar routes such as `/ipfs/`, `/ipns/`, and the supported `/api/v0/` proxy through its own TLS, authentication, API-key, and rate-protection layers. + +### CID and IPNS pinning + +Static CID pinning is built in, but watched IPNS pinning is one of TruthGate's most useful features. + +![CID pin management](content/images/demo/pinned-dark.webp) + +An operator can subscribe to an IPNS name and let TruthGate resolve it on a schedule. When the name changes, TruthGate can pin the new target and apply the configured retention behavior to older targets. That turns a mutable IPNS identity into a continuously maintained local copy instead of a manual sequence of resolve-and-pin commands. + +![Watched IPNS pins](content/images/demo/pinned-ipns-dark.webp) + +### TruthGate Pointer protocol + +TruthGate supports the **TruthGate Pointer protocol (TGP)**: a deliberately small convention in which an IPNS identity publishes a tiny `tgp.json` file that points to one current immutable CID. + +TGP keeps the mutable layer light, makes freshness checks inexpensive, allows the current target to be cached normally, and supports clear unpin-and-garbage-collection workflows when content is removed. It intentionally does not publish a browsable history through the pointer. + +TGP is an operational protocol, not a legal shield. It cannot erase copies retained by third parties, prevent archiving, or make an operator immune from legal obligations. See the [TGP documentation](docs/tgp/index.md) for the specification, rationale, gateway behavior, and legal limitations. + +### Static-site publishing + +TruthGate publishes static output such as HTML, CSS, JavaScript, WebAssembly, images, and other immutable assets to IPFS. It is suitable for ordinary static sites, SPAs, Blazor WebAssembly applications, and other frameworks whose production output can be served as files. + +The publishing flow connects: + +1. a build directory or uploaded site; +2. a content-addressed site CID; +3. optional IPNS and TGP metadata; +4. one or more mapped domains; +5. automatic certificate handling and site routing. + +[Read the site-publishing guide](docs/site-publishing.md). + +### Domains, routing, and automatic TLS + +Mapped domains are first-class configuration. TruthGate terminates TLS, obtains and renews ACME certificates, selects certificates by SNI, and routes mapped hosts to their published IPFS content. + +An IP address or unmapped management host serves the authenticated TruthGate application. A mapped site host follows the site-serving path instead. This distinction allows the same appliance to provide an operator console and public websites without exposing Kubo directly. + +![Domain management](content/images/demo/domains-dark.webp) + +![Publishing a mapped domain](content/images/demo/domains-publish-dark.webp) + +### Public metadata and automation + +Published domains can expose small read-only metadata endpoints that report the current site CID and IPNS/TGP state. Monitoring tools, deployment checks, and client applications can use these values without receiving private keys or administrative access. + +Authenticated integrations can use API keys for supported Kubo and TruthGate operations. + +![API-key management](content/images/demo/api-dark.webp) + +![Creating an API key](content/images/demo/api-add-dark.webp) + +### One Docker appliance + +The production image contains: + +- the TruthGate ASP.NET application; +- the pinned Kubo version; +- the matching `ipfs` CLI; +- startup, migration, health, and diagnostic tooling. + +The appliance is published for `linux/amd64` and `linux/arm64`. Its root filesystem is read-only in the production Compose definition; writable application state, Kubo repository data, and Kubo blocks use persistent mounts. + +### Contributing Kubo defaults + +A new repository is initialized as a contributing server rather than a quiet desktop node. Defaults include DHT server routing, inbound TCP/QUIC/WebTransport listeners, hole punching, relay-client support, content providing, automatic storage sizing, and repository garbage collection. + +These defaults can be overridden persistently. The Kubo RPC API and HTTP gateway remain loopback-only inside the container. + +### Persistence, updates, and migrations + +TruthGate separates three persistent host paths: + +```text +data/ +├── truthgate/ # application config, database, certificates, secrets, state +└── ipfs/ + ├── repo/ # Kubo identity, config, datastore, keystore, repo version + └── blocks/ # block files +``` + +Replacing the image does not replace those mounts. Startup checks the existing Kubo repository version and performs supported repository migration before applying managed settings. + +### Rate limiting and abuse protection + +TruthGate places request protection before expensive proxy and application work. Public endpoints, authenticated administrative paths, and gateway routes have separate policies while sharing ban, whitelist, and IPv6-prefix state. + +The documentation describes the current defaults and their limits without pretending that rate limiting is a substitute for network security, monitoring, or capacity planning. + +## What TruthGate is not + +- TruthGate does not replace IPFS or Kubo. +- TruthGate is not a blockchain or a new content network. +- TruthGate does not make third-party copies of a CID disappear. +- TGP does not provide legal immunity or guaranteed global deletion. +- A mapped HTTPS domain is still an HTTP delivery path; the content remains independently addressable by CID. +- TruthGate is not intended to expose Kubo's unrestricted local RPC interface directly to anonymous internet users. + +## Architecture at a glance + +```text +Operators, browsers, and API clients + │ + ▼ +┌─────────────────────────────────────────────┐ +│ TruthGate │ +│ │ +│ Accounts • Sessions • API keys • TLS │ +│ Domains • Publishing • Pinning • TGP │ +│ Rate protection • Metadata • Diagnostics │ +└──────────────────────┬──────────────────────┘ + │ loopback-only API/gateway + ▼ +┌─────────────────────────────────────────────┐ +│ Kubo │ +│ │ +│ Repository • Blocks • DHT • IPNS • Bitswap │ +└──────────────────────┬──────────────────────┘ + │ + ▼ + IPFS network +``` + +Read [Architecture](docs/concepts/architecture.md), [Request routing](docs/concepts/request-routing.md), and [Security boundaries](docs/concepts/security-boundaries.md) for the detailed model. + +## Quick start + +### Requirements + +- Docker Engine +- Docker Compose v2.24.4 or newer +- TCP ports `80`, `443`, and `4001` +- UDP port `4001` + +Clone the repository and start the tested stable image: ```bash git clone https://github.com/magiccodingman/TruthGate-IPFS.git @@ -30,94 +183,93 @@ cd TruthGate-IPFS cp .env.example .env docker compose pull docker compose up -d -docker compose logs truthgate ``` -The default Compose image is the tested multi-platform stable release: +Watch the first startup: -```text -magiccodingman/truthgate-ipfs:stable +```bash +docker compose logs -f truthgate ``` -The first-start logs contain a generated password for the `admin` account. -Open `https://localhost`, accept the temporary self-signed fallback certificate, -and change the password. - -The appliance configures Kubo as a contributing server by default: DHT server -mode, TCP/QUIC/WebTransport swarm listeners, content providing, automatic -storage sizing, and repository GC are enabled with persistent per-setting -overrides. Inspect the live node with: +The log prints: -```bash -docker exec truthgate truthgate-kubo-status +```text +First-run administrator account: admin +First-run administrator password: ``` -For the full persistence contract, Kubo settings, release tags, image update -flow, ARM64/AMD64 publishing, and Docker-based development setup, see -**[DOCKER.md](DOCKER.md)**. +Open `https://YOUR_SERVER_IP`, accept the temporary self-signed fallback certificate, sign in, and change the password. -To build the production image locally instead of pulling the stable release: +The bootstrap password is also retained temporarily at: -```bash -docker compose up --build -d +```text +data/truthgate/state/bootstrap-admin-password ``` -Development with hot reload uses the production definition plus a small -override: +It is removed after the persistent TruthGate configuration exists. + +Check the container and Kubo state: ```bash -docker compose -f compose.yaml -f compose.dev.yaml up --build +docker compose ps +docker exec truthgate truthgate-kubo-status ``` -Then open `http://localhost:8080`. - ---- +Continue with [Docker installation](docs/setup/docker.md) and [First run](docs/setup/first-run.md). -## What Is It? +## Updating -TruthGate is a **secure edge layer for IPFS nodes**. -Think **Netlify, but for IPFS**, self-hosted, login-protected, and actually yours. - -It solves the problems developers hit with IPFS: +```bash +docker compose pull +docker compose up -d +``` -- Node exposure risks -- SSL/domain linking headaches -- CLI-only publishing -- Zero protection for `/api` or `/webui` routes -- Reliance on public gateways +The normal Compose configuration follows: -[See how it works](https://truthgate.io) +```text +magiccodingman/truthgate-ipfs:stable +``` ---- +Pin `TRUTHGATE_IMAGE` to an exact version such as `magiccodingman/truthgate-ipfs:0.1.2` when reproducibility matters more than following the moving stable channel. -## Get Started +See [Updates and image tags](docs/setup/updating.md). -Head to **[truthgate.io](https://truthgate.io)** for installation, configuration, and publishing guides. +## Development ---- +Development uses the production Compose file first and the development override second: -## A Note from the Creator +```bash +docker compose \ + -f compose.yaml \ + -f compose.dev.yaml \ + up --build +``` -I built this out of frustration. +The two files are merged. `compose.yaml` defines the real appliance contract; `compose.dev.yaml` changes only what development requires, including the build target, source mount, hot reload, HTTP port, writable filesystem, and NuGet caches. -I wanted a way to serve Web3-native apps that *actually worked*, securely, reliably, and without selling my soul to centralized hosts. Now it’s real. +New production behavior belongs in `compose.yaml`, not only in the development override. -If you’ve ever wrestled with IPFS routing, SSL certs, or gateway hacks just to get your site online, **TruthGate is for you.** +See the [Developer documentation](docs/developer/index.md), including [Rider setup](docs/developer/rider.md), Compose conventions, testing guidance, and the intentionally provisional Visual Studio page. ---- +## Documentation -## Contribute +Start at **[docs/index.md](docs/index.md)**. -Pull requests welcome. -Stars encourage me. -Issues are sacred. +- [Installation and operations](docs/setup/index.md) +- [Developer guide](docs/developer/index.md) +- [Site publishing](docs/site-publishing.md) +- [Concepts and architecture](docs/concepts/index.md) +- [API documentation](docs/api/index.md) +- [TGP protocol](docs/tgp/index.md) +- [Reference](docs/reference/index.md) +- [Troubleshooting](docs/troubleshooting.md) -Let’s fix decentralized hosting together. +## Project status -[truthgate.io](https://truthgate.io) +TruthGate is active open-source infrastructure. Interfaces and behavior may continue to evolve, especially around publishing, APIs, and advanced Kubo orchestration. Exact release tags are available for deployments that should not follow moving channels. ---- +Issues and pull requests are welcome. Documentation changes should describe current behavior, clearly label planned behavior, and avoid security or legal guarantees that the implementation cannot make. -## Creator +## License and author -Solo development done by [MagicCodingMan](https://github.com/magiccodingman), but under the TruthOrigin umbrella. +TruthGate is maintained by [MagicCodingMan](https://github.com/magiccodingman). See the repository license for usage terms. diff --git a/docs/api/authentication.md b/docs/api/authentication.md new file mode 100644 index 0000000..9d3837b --- /dev/null +++ b/docs/api/authentication.md @@ -0,0 +1,47 @@ +# API authentication + +## API keys + +Create keys from the authenticated API settings page. + +![Creating an API key](../../content/images/demo/api-add-dark.webp) + +A key is displayed only when created. Store it in a password manager or secret store. + +TruthGate stores a hash rather than a recoverable copy. Losing the original value requires creating a new key. + +## Header + +Preferred: + +```http +X-API-Key: YOUR_KEY +``` + +## Query string + +Legacy or convenience forms may be accepted on supported routes: + +```text +?api_key=YOUR_KEY +?key=YOUR_KEY +``` + +Headers are preferred because URLs are more likely to appear in browser history, proxy logs, analytics, screenshots, and copied links. + +## Session authentication + +Browser-based management uses the login session. Do not build automation that scrapes login forms when API keys are available. + +## Responses + +Invalid, missing, and suspended credentials should not reveal which condition occurred. A banned client may be rejected before credential processing. + +## Rotation + +1. Create a replacement key. +2. Update the client. +3. Verify successful requests. +4. Revoke the old key. + +Never commit API keys to source control or `.env.example`. diff --git a/docs/api/domain-metadata.md b/docs/api/domain-metadata.md new file mode 100644 index 0000000..8beb088 --- /dev/null +++ b/docs/api/domain-metadata.md @@ -0,0 +1,81 @@ +# Public domain metadata API + +Published domains expose read-only deployment metadata. + +Base path: + +```text +https://YOUR_DOMAIN/api/truthgate/v1 +``` + +Call these endpoints on the published domain. The Host header identifies the domain configuration. + +## Current domain CID + +```http +GET /api/truthgate/v1/GetDomainCid +``` + +Example: + +```json +{ + "domain": "example.com", + "cidV0": "QmExample...", + "cidV1": "bafyExample..." +} +``` + +Use this endpoint for: + +- deployment verification; +- gateway URLs; +- monitoring; +- CID-aware clients. + +## Current IPNS and TGP state + +```http +GET /api/truthgate/v1/GetDomainIpns +``` + +Example: + +```json +{ + "domain": "example.com", + "ipnsPeerId": "k51...", + "tgpCid": "bafy...", + "currentCid": "bafy...", + "lastPublishedCid": "bafy..." +} +``` + +Field meaning: + +- `ipnsPeerId` — the configured IPNS identity; +- `tgpCid` — the CID of the current TGP root; +- `currentCid` — the active target from `tgp.json`; +- `lastPublishedCid` — the latest site root recorded by TruthGate. + +## Examples + +```bash +curl -s \ + https://example.com/api/truthgate/v1/GetDomainCid \ + | jq +``` + +```bash +curl -s \ + https://example.com/api/truthgate/v1/GetDomainIpns \ + | jq +``` + +## Security model + +These endpoints are anonymous and intentionally reveal public deployment pointers. They do not return private keys, account data, or API credentials. + +## Caching + +Metadata may be cached briefly. Monitoring should tolerate a short delay after publication and compare both the immutable site CID and mutable IPNS/TGP state. diff --git a/docs/api/errors-and-rate-limits.md b/docs/api/errors-and-rate-limits.md new file mode 100644 index 0000000..4b20e39 --- /dev/null +++ b/docs/api/errors-and-rate-limits.md @@ -0,0 +1,43 @@ +# API errors and rate limits + +## Common status codes + +| Status | Meaning | +|---:|---| +| `400` | Invalid request or host | +| `401` | Missing or invalid authentication | +| `403` | Access denied or client banned | +| `404` | Route, domain, site, or content not found | +| `429` | Rate limit exceeded | +| `500` | Internal failure | +| `502` / `503` | Upstream Kubo or temporary service failure | + +Error bodies should avoid revealing credential validity, ban duration, internal paths, or private configuration. + +## Public-controller defaults + +The default public per-IP minute budget starts at `300` and tightens with global traffic tiers: + +| Global protected requests in the previous hour | Per-IP minute budget | +|---:|---:| +| below `2,000` | `300` | +| `2,000` or more | `200` | +| `8,000` or more | `100` | +| `16,000` or more | `30` | + +## Gateway defaults + +| Setting | Default | +|---|---:| +| Free requests per minute per IP | `400` | +| Sliding hourly overage | `3,200` | +| Ban after overage exhaustion | `4 hours` | +| Auto-whitelist after authenticated use | enabled | + +These are application defaults, not a promise that every deployment has unchanged values. + +## Retry behavior + +A `429` client should stop, honor `Retry-After` when present, and retry with backoff. + +Do not aggressively retry `401` or `403` responses. diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 0000000..ea467de --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,33 @@ +# API documentation + +TruthGate has more than one API surface. + +## Authenticated Kubo proxy + +TruthGate exposes supported Kubo RPC calls through `/api/v0/` while keeping Kubo's local RPC listener private. + +Read [Kubo proxy](kubo-proxy.md) and [Authentication](authentication.md). + +## Public domain metadata + +Published domains expose read-only metadata about the current CID and IPNS/TGP state. + +Read [Domain metadata](domain-metadata.md). + +## TruthGate application APIs + +The management UI and internal services use TruthGate-specific endpoints. These are not all guaranteed as a stable public integration contract. + +Publicly documented integrations should be limited to routes that are intentionally supported and tested. When a new endpoint becomes part of the external contract, document: + +- method and path; +- authentication; +- request schema; +- response schema; +- errors; +- rate policy; +- compatibility guarantees. + +## Errors and limits + +Read [Errors and rate limits](errors-and-rate-limits.md). diff --git a/docs/api/kubo-proxy.md b/docs/api/kubo-proxy.md new file mode 100644 index 0000000..2ce3986 --- /dev/null +++ b/docs/api/kubo-proxy.md @@ -0,0 +1,47 @@ +# Kubo API proxy + +TruthGate can proxy supported Kubo RPC operations under: + +```text +/api/v0/ +``` + +The local Kubo RPC API remains: + +```text +/ip4/127.0.0.1/tcp/5001 +``` + +inside the appliance. + +## Why proxy it + +Kubo's RPC API is an administrative interface. Publishing it directly would bypass TruthGate's: + +- TLS termination; +- account and API-key policy; +- rate protection; +- audit and error behavior; +- route restrictions. + +## Example + +```bash +curl \ + -H 'X-API-Key: YOUR_KEY' \ + 'https://YOUR_HOST/api/v0/id' +``` + +Exact methods and parameters follow the Kubo API for operations TruthGate permits. + +## Compatibility + +TruthGate bundles a specific Kubo version. Check `.env.example` or the image labels before relying on a Kubo method introduced in a newer release. + +## Scope + +Do not assume every Kubo RPC route is safe or supported through the public proxy. External documentation should name supported operations explicitly as that contract is stabilized. + +## Native WebUI + +The native WebUI uses Kubo APIs through the authenticated TruthGate path. It should be opened from the portal rather than by publishing Kubo's local gateway. diff --git a/docs/concepts/architecture.md b/docs/concepts/architecture.md new file mode 100644 index 0000000..a8acb72 --- /dev/null +++ b/docs/concepts/architecture.md @@ -0,0 +1,63 @@ +# Architecture + +TruthGate is an edge and management layer around Kubo. + +## Components + +### TruthGate application + +The ASP.NET application provides: + +- account and session authentication; +- API-key management; +- Blazor management UI; +- TLS and certificate selection; +- domain routing; +- publishing and pinning orchestration; +- request protection; +- metadata endpoints; +- Kubo proxy endpoints. + +### Kubo + +Kubo provides: + +- content addressing; +- block storage and exchange; +- DHT routing; +- IPNS publication and resolution; +- repository and key management; +- the native WebUI; +- local RPC and gateway services. + +### Container entrypoint + +The entrypoint: + +1. creates required directories; +2. validates paths; +3. handles the first-run administrator password; +4. migrates an existing Kubo repository; +5. applies managed Kubo settings; +6. starts Kubo; +7. waits for the loopback RPC API; +8. starts TruthGate; +9. supervises both processes. + +If either managed process exits, the entrypoint stops the other and exits so Docker can restart the appliance. + +## Trust boundary + +Only TruthGate is published as the HTTP edge. + +Kubo RPC and gateway listeners remain on loopback. TruthGate calls them from inside the container and decides which behavior is available to an authenticated operator, API key, mapped site domain, or public metadata request. + +## Application rendering + +The management application uses interactive Blazor Server and WebAssembly components. The production publish must include the `blazor.web.js` bootstrap asset; CI validates that the deployed asset is nontrivial. + +## Data boundary + +The image is disposable. Persistent state lives under `/data/truthgate` and `/data/ipfs`. + +See [Persistence](persistence.md). diff --git a/docs/concepts/index.md b/docs/concepts/index.md new file mode 100644 index 0000000..0437950 --- /dev/null +++ b/docs/concepts/index.md @@ -0,0 +1,11 @@ +# Concepts + +These pages explain how TruthGate fits together. + +- [Architecture](architecture.md) +- [Request routing](request-routing.md) +- [Pinning, IPNS, and publishing](pinning-ipns-and-publishing.md) +- [Security boundaries](security-boundaries.md) +- [Persistence](persistence.md) + +Use the setup guides for commands and these pages for the mental model. diff --git a/docs/concepts/persistence.md b/docs/concepts/persistence.md new file mode 100644 index 0000000..cd95f7d --- /dev/null +++ b/docs/concepts/persistence.md @@ -0,0 +1,41 @@ +# Persistence + +TruthGate treats the container image as disposable and the mounted data as the appliance identity. + +## TruthGate state + +`/data/truthgate` includes: + +- configuration; +- database; +- certificates; +- ACME account data; +- secrets and data-protection keys; +- first-run and migration state. + +## Kubo repository + +`/data/ipfs/repo` includes: + +- peer identity; +- Kubo configuration; +- datastore metadata; +- keystore and IPNS keys; +- repository version; +- logical `blocks` path. + +## Blockstore + +`/data/ipfs/repo/blocks` is mounted separately from the host path configured by `IPFS_BLOCKS_HOST_PATH`. + +## Replacement + +`docker compose up -d` can replace the container while preserving the same mounts. The new process sees the existing identity and state. + +## Migration + +Repository format migration changes persistent data. A container rollback does not necessarily downgrade that data. Back up before updates that change Kubo repository versions. + +## Permissions + +The appliance runs managed processes as UID/GID `1000` by default. Startup prepares mount roots, but a manually restored tree may still need ownership correction. diff --git a/docs/concepts/pinning-ipns-and-publishing.md b/docs/concepts/pinning-ipns-and-publishing.md new file mode 100644 index 0000000..06a159d --- /dev/null +++ b/docs/concepts/pinning-ipns-and-publishing.md @@ -0,0 +1,53 @@ +# Pinning, IPNS, and publishing + +![Watched IPNS pins](../../content/images/demo/pinned-ipns-dark.webp) + +## CID pins + +A CID identifies immutable content. Pinning tells the local Kubo repository to retain the reachable blocks instead of allowing garbage collection to remove them. + +## IPNS + +IPNS gives a stable, signed identity whose value can point to different content over time. + +An IPNS name is not itself a local retention policy. Resolving a name does not guarantee that your node keeps the target. + +## Watched IPNS pinning + +TruthGate can subscribe to an IPNS identity. + +A background worker: + +1. resolves the name; +2. determines the current target; +3. pins the target; +4. records state; +5. applies configured retention behavior; +6. repeats after the scheduled cooldown. + +This is useful for mirrors, deployment nodes, regional replicas, and applications that need the current version available locally. + +## Retention choices + +Two broad policies exist: + +- retain previously observed targets; +- keep the current target and unpin older targets. + +Neither policy can control blocks held by third parties. + +## Publishing + +Publishing creates content and optionally updates a mutable identity you control. + +Watching and publishing can be combined: + +- one TruthGate instance publishes; +- other instances watch the IPNS name; +- each watcher pins the current CID. + +## TGP + +TGP makes the mutable IPNS payload a small pointer to the current CID instead of making the IPNS root the complete site. + +Read [TGP rationale](../tgp/rationale.md). diff --git a/docs/concepts/request-routing.md b/docs/concepts/request-routing.md new file mode 100644 index 0000000..d282bf3 --- /dev/null +++ b/docs/concepts/request-routing.md @@ -0,0 +1,70 @@ +# Request routing + +TruthGate decides whether a request belongs to the management application, a published site, a protected Kubo route, or a controller endpoint. + +## Management requests + +Requests by IP address are not treated as mapped site domains. They continue to the authenticated TruthGate application. + +An unmapped hostname follows the same management path. + +Typical examples: + +```text +https://203.0.113.10/ +https://admin.example.net/ +``` + +## Mapped site requests + +When the Host header matches a configured site domain, TruthGate routes the request to that site's published IPFS content. + +Typical example: + +```text +https://www.example.com/ +``` + +The site path does not render the management portal. + +## Protected Kubo routes + +TruthGate can expose supported forms of: + +```text +/ipfs/ +/ipns/ +/api/v0/ +/webui/ +``` + +These pass through TruthGate's authentication, API-key, TLS, and request-protection behavior rather than publishing Kubo's local listeners directly. + +## Public metadata + +Small read-only domain metadata endpoints are intentionally anonymous. They reveal deployment pointers such as CIDs and IPNS/TGP state, not private keys. + +## Static framework assets and Blazor circuits + +The management host must serve: + +```text +/_framework/ +/_content/ +/_blazor +``` + +These are TruthGate application assets and circuit endpoints. They are unrelated to the assets of a separately published site. + +## Order matters + +The pipeline applies error handling, request protection, authentication, controllers, mapped-domain behavior, management-host guarding, static assets, and Razor components in deliberate order. + +Changing middleware order can alter security or make a site route capture management traffic. Route changes should include tests for: + +- raw IP portal; +- unmapped management host; +- mapped site host; +- authenticated Kubo routes; +- anonymous metadata; +- Blazor negotiate and WebSocket behavior. diff --git a/docs/concepts/security-boundaries.md b/docs/concepts/security-boundaries.md new file mode 100644 index 0000000..394da5e --- /dev/null +++ b/docs/concepts/security-boundaries.md @@ -0,0 +1,60 @@ +# Security boundaries + +TruthGate reduces the need to expose Kubo directly, but it is not a claim of complete security. + +## Publicly reachable + +In the standard deployment: + +- TruthGate HTTP/HTTPS; +- Kubo swarm TCP/UDP. + +## Loopback-only inside the appliance + +- Kubo RPC API on port `5001`; +- Kubo gateway on port `9010`. + +## Authentication modes + +### Browser session + +Users sign in through the management portal. Session cookies protect interactive management behavior. + +### API key + +Integrations can use scoped or supported API credentials. Keys are shown once and stored as hashes. + +### Anonymous metadata + +Selected domain metadata is public by design. It should not expose secrets. + +### Mapped site traffic + +A public website is not expected to require an operator login merely to read published files. The domain-routing path is separate from the management portal. + +## Native WebUI + +The native IPFS WebUI is powerful because it can operate the node. TruthGate exposes it through authenticated routing instead of publishing the local Kubo interface. + +## TLS + +TruthGate terminates TLS. A self-signed certificate is used for IP addresses and unknown hosts; configured domains can receive ACME certificates. + +TLS protects transport to TruthGate. It does not change the public nature of a CID shared on IPFS. + +## Rate protection + +Rate limiting, bans, whitelists, and TLS-churn detection reduce obvious abuse and expensive request patterns. They do not replace: + +- host firewalling; +- updates; +- monitoring; +- resource limits; +- upstream DDoS protection; +- secure passwords; +- key rotation; +- backups. + +## Forwarded headers + +An upstream proxy can affect client IP, scheme, and host interpretation. Only trust forwarded headers from infrastructure you control. The standard deployment avoids this ambiguity by publishing TruthGate directly. diff --git a/docs/developer/docker-compose.md b/docs/developer/docker-compose.md new file mode 100644 index 0000000..978b6e9 --- /dev/null +++ b/docs/developer/docker-compose.md @@ -0,0 +1,85 @@ +# Development Compose model + +TruthGate uses two Compose files for development: + +```text +compose.yaml +compose.dev.yaml +``` + +They are passed in that order: + +```bash +docker compose \ + -f compose.yaml \ + -f compose.dev.yaml \ + up --build +``` + +## Merge behavior + +`compose.yaml` defines the production service: + +- production build target; +- stable image; +- ports; +- persistent paths; +- environment; +- security options; +- health check. + +`compose.dev.yaml` merges into that service and changes development-specific behavior: + +- development build target; +- `development` command; +- no restart policy; +- writable root filesystem; +- repository mounted at `/workspace`; +- `dotnet watch`; +- NuGet cache volumes; +- HTTP portal on host `8080`; +- development environment names. + +The override uses Compose `!override` where a list must replace rather than append to the production list. + +## Where new configuration belongs + +Add a new production requirement to `compose.yaml`. + +Examples: + +- new persistent mount; +- new required environment variable; +- new production port; +- new health-check dependency; +- new container capability. + +Add only the development difference to `compose.dev.yaml`. + +A development-only addition that never reaches `compose.yaml` is easy to forget when opening a PR and can create a feature that works only on a developer machine. + +## Validate merges + +```bash +docker compose -f compose.yaml config +``` + +```bash +docker compose \ + -f compose.yaml \ + -f compose.dev.yaml \ + config +``` + +Review the merged result when changing ports, lists, volumes, or security settings. + +## Stop + +```bash +docker compose \ + -f compose.yaml \ + -f compose.dev.yaml \ + down +``` + +Do not add `-v` unless deleting the development volumes and data is intentional. diff --git a/docs/developer/documentation.md b/docs/developer/documentation.md new file mode 100644 index 0000000..1c21b5a --- /dev/null +++ b/docs/developer/documentation.md @@ -0,0 +1,57 @@ +# Documentation contributions + +## Structure + +Every directory under `docs/` must contain `index.md`. + +Use: + +- setup pages for tasks; +- concepts pages for mental models; +- API pages for contracts; +- reference pages for exact values; +- developer pages for contributor workflows; +- TGP pages for protocol material. + +## Source of truth + +Verify exact routes, defaults, environment variables, and limits against current source before documenting them. + +Do not copy old website text without checking it. + +## Language + +Prefer concrete statements: + +> Kubo's RPC API is bound to loopback. + +Avoid vague guarantees: + +> The API is completely secure. + +## Planned behavior + +Label it explicitly: + +> **Planned:** TruthGate may proxy additional swarm transports in a future architecture. + +## Prohibited stale strings + +Do not reintroduce: + +- old organization/repository branding; +- shared default administrator passwords; +- old bare-metal auto-update scripts; +- claims that TruthGate cannot run in Docker; +- outdated runtime or UI-architecture labels as the current architecture; +- legal-immunity or guaranteed-deletion claims. + +## Images + +Use repository-relative paths to `content/images/demo/`. + +Images should explain a nearby feature and include useful alt text. + +## Links + +Use relative links for repository documentation so they work on GitHub and Docker Hub's synchronized README. diff --git a/docs/developer/index.md b/docs/developer/index.md new file mode 100644 index 0000000..3523062 --- /dev/null +++ b/docs/developer/index.md @@ -0,0 +1,47 @@ +# Developer guide + +Development uses the same appliance contract as production. + +## Start + +```bash +docker compose \ + -f compose.yaml \ + -f compose.dev.yaml \ + up --build +``` + +Open: + +```text +http://localhost:8080 +``` + +Retrieve the first-run password: + +```bash +docker compose \ + -f compose.yaml \ + -f compose.dev.yaml \ + logs truthgate +``` + +## Guides + +- [Compose model](docker-compose.md) +- [Rider setup](rider.md) +- [Visual Studio](visual-studio.md) +- [Testing](testing.md) +- [Documentation contributions](documentation.md) + +## Important local rule + +Do not run another Kubo/IPFS Desktop node on the same machine with the default ports while debugging TruthGate. Both will normally want swarm TCP and UDP port `4001`, and conflicting local gateways or RPC assumptions can make failures confusing. + +Stop the other node or deliberately change the development swarm host port. + +## Source layout principle + +Production behavior belongs in the production definitions and source. Development overrides should only change what is required to develop. + +A feature that needs a new environment variable, port, volume, or runtime dependency should normally be added to `compose.yaml` first and then adjusted in `compose.dev.yaml` only when development differs. diff --git a/docs/developer/rider.md b/docs/developer/rider.md new file mode 100644 index 0000000..3c58a86 --- /dev/null +++ b/docs/developer/rider.md @@ -0,0 +1,73 @@ +# Rider development setup + +These steps describe the tested JetBrains Rider workflow. + +## Create a Docker Compose run configuration + +1. Open the TruthGate repository in Rider. +2. Open **Run | Edit Configurations**. +3. Add a **Docker Compose** configuration. +4. Set the service to: + +```text +truthgate +``` + +5. Set the Compose files, in order, to: + +```text +./compose.yaml +./compose.dev.yaml +``` + +Depending on Rider's field presentation, the combined value may appear as: + +```text +./compose.yaml; ./compose.dev.yaml; +``` + +The order matters. Production is the base; development is the override. + +6. Enable image build before startup. +7. Run the configuration. + +## Open TruthGate + +```text +http://localhost:8080 +``` + +## Retrieve the generated password + +From Rider's container logs, or: + +```bash +docker compose \ + -f compose.yaml \ + -f compose.dev.yaml \ + logs truthgate +``` + +Find the generated `admin` password. + +## Hot reload + +The repository is mounted at `/workspace`. The development entrypoint runs `dotnet watch` for: + +```text +/workspace/TruthGate-Web/TruthGate-Web/TruthGate-Web.csproj +``` + +NuGet package and HTTP caches use Docker volumes so normal rebuilds do not redownload everything. + +## Debugging considerations + +Rider can attach to or launch within the development container depending on local configuration. The important invariant is that the Compose service, mounts, environment, and Kubo instance match the documented development appliance. + +## Avoid another local IPFS node + +Stop IPFS Desktop or another Kubo daemon before using the default development setup. Swarm TCP and UDP port `4001` conflicts can prevent startup or produce misleading connectivity results. + +## Reset development state + +Stop the stack, move the local `data/` directory aside, and restart. Do not delete it unless losing the development identity, keys, pins, users, and configuration is intended. diff --git a/docs/developer/testing.md b/docs/developer/testing.md new file mode 100644 index 0000000..9e0e450 --- /dev/null +++ b/docs/developer/testing.md @@ -0,0 +1,76 @@ +# Testing + +## Local development matrix + +Test: + +- management portal by IP/localhost; +- login and logout; +- authenticated native IPFS WebUI; +- CID pin management; +- watched IPNS updates; +- API-key creation and use; +- mapped domain routing; +- SPA fallback; +- public domain metadata; +- TGP validation; +- container recreation with persistent state. + +## Browser contexts + +Useful separate contexts include: + +- normal browser profile; +- private/incognito profile; +- IPFS-aware browser profile when testing Companion behavior. + +Do not rely on one cached authenticated browser session for every routing test. + +## Container checks + +```bash +docker compose ps +docker exec truthgate truthgate-kubo-status +``` + +## Build targets + +Test both: + +```bash +docker build --target production . +docker build --target development . +``` + +## Compose validation + +```bash +docker compose -f compose.yaml config +docker compose -f compose.yaml -f compose.dev.yaml config +``` + +## Blazor regression check + +```bash +curl -skf \ + -H 'Accept-Encoding: identity' \ + https://127.0.0.1/_framework/blazor.web.js \ + -o /tmp/blazor.web.js + +test "$(wc -c +ipfs cat /ipfs//index.html +``` + +This verifies more than local RPC health. diff --git a/docs/developer/visual-studio.md b/docs/developer/visual-studio.md new file mode 100644 index 0000000..d661a17 --- /dev/null +++ b/docs/developer/visual-studio.md @@ -0,0 +1,32 @@ +# Visual Studio development setup + +**Status: similar workflow expected; full instructions not yet verified.** + +TruthGate development is based on: + +```text +compose.yaml +compose.dev.yaml +``` + +with the production file first and the development override second. + +Visual Studio supports Docker Compose projects and should be able to launch the same `truthgate` service, but the maintainer has not yet completed and verified an end-to-end Visual Studio configuration. + +Until that happens: + +- use the command-line Compose workflow; or +- configure Visual Studio using the same file order and service; +- compare the final merged Compose configuration; +- do not submit untested IDE-specific instructions as authoritative documentation. + +Contributions are welcome from someone who has verified: + +- build target selection; +- source mounting; +- generated-password retrieval; +- debugger attachment; +- hot reload; +- stop/recreate behavior; +- persistent data handling; +- AMD64 and ARM64 limitations. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..dfa68d1 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,66 @@ +# TruthGate documentation + +TruthGate is a self-hosted edge gateway, management plane, and static-site publishing system for Kubo/IPFS. + +This documentation is organized by task: + +| I want to… | Start here | +|---|---| +| Install a production node | [Setup](setup/index.md) | +| Sign in for the first time | [First run](setup/first-run.md) | +| Publish a static website | [Site publishing](site-publishing.md) | +| Understand the architecture | [Concepts](concepts/index.md) | +| Integrate through APIs | [API documentation](api/index.md) | +| Implement or inspect TGP | [TGP protocol](tgp/index.md) | +| Develop TruthGate | [Developer guide](developer/index.md) | +| Look up exact defaults | [Reference](reference/index.md) | +| Diagnose a problem | [Troubleshooting](troubleshooting.md) | + +![TruthGate dashboard](../content/images/demo/dashboard-dark.webp) + +## Documentation principles + +The repository documentation describes behavior that exists in the current codebase. + +- Planned behavior is labeled **planned**. +- Security mechanisms are described concretely rather than as guarantees. +- TGP is documented as an operational protocol, not legal immunity. +- Production instructions are Docker-first. +- Every documentation directory has an `index.md`. +- Exact values belong in reference pages so explanatory pages do not duplicate them. + +## Core product areas + +### Management and access + +TruthGate provides account login, role-based users, session authentication, API-key management, and an authenticated path to the native IPFS WebUI. + +### IPFS operations + +TruthGate manages CID pins, watched IPNS names, current-target repinning, Kubo configuration, storage policy, repository migration, content providing, and node diagnostics. + +### Publishing + +Static output can be published to IPFS, associated with IPNS/TGP metadata, and served through mapped domains with automatic TLS and SPA-aware routing. + +### Protocols and APIs + +TruthGate exposes selected authenticated Kubo operations, public domain metadata, and the TruthGate Pointer protocol. + +## Recommended reading order + +For an operator: + +1. [Docker installation](setup/docker.md) +2. [First run](setup/first-run.md) +3. [Networking](setup/networking.md) +4. [Storage and backups](setup/storage-and-backups.md) +5. [Site publishing](site-publishing.md) + +For a developer: + +1. [Architecture](concepts/architecture.md) +2. [Request routing](concepts/request-routing.md) +3. [Developer guide](developer/index.md) +4. [API index](api/index.md) +5. [TGP specification](tgp/specification-v1.md) diff --git a/docs/reference/environment-variables.md b/docs/reference/environment-variables.md new file mode 100644 index 0000000..75f740c --- /dev/null +++ b/docs/reference/environment-variables.md @@ -0,0 +1,60 @@ +# Environment variables + +## Image and build + +| Variable | Default | Purpose | +|---|---|---| +| `TRUTHGATE_IMAGE` | `magiccodingman/truthgate-ipfs:stable` | Image pulled by Compose | +| `DOTNET_VERSION` | `10.0` | Docker build input | +| `KUBO_VERSION` | `v0.42.0` | Docker build input | +| `TRUTHGATE_UID` | `1000` | Runtime user ID | +| `TRUTHGATE_GID` | `1000` | Runtime group ID | + +## Host ports + +| Variable | Default | +|---|---:| +| `TRUTHGATE_HTTP_PORT` | `80` | +| `TRUTHGATE_HTTPS_PORT` | `443` | +| `TRUTHGATE_DEV_HTTP_PORT` | `8080` | +| `IPFS_SWARM_PORT` | `4001` | + +## Host paths + +| Variable | Default | +|---|---| +| `TRUTHGATE_DATA_HOST_PATH` | `./data/truthgate` | +| `IPFS_REPO_HOST_PATH` | `./data/ipfs/repo` | +| `IPFS_BLOCKS_HOST_PATH` | `./data/ipfs/blocks` | + +## Certificates + +| Variable | Default | Purpose | +|---|---|---| +| `TRUTHGATE_ACME_STAGING` | `false` | Use ACME staging | +| `TRUTHGATE_CERT_IPS` | empty | Comma-separated fallback-certificate IPs | + +## Kubo policy + +| Variable | Default behavior | +|---|---| +| `TRUTHGATE_KUBO_APPLY_SERVER_PROFILE` | enabled | +| `TRUTHGATE_KUBO_ENSURE_SWARM_LISTENERS` | enabled | +| `TRUTHGATE_KUBO_ROUTING_TYPE` | `dhtserver` | +| `TRUTHGATE_KUBO_HOLE_PUNCHING` | enabled | +| `TRUTHGATE_KUBO_RELAY_CLIENT` | enabled | +| `TRUTHGATE_KUBO_PROVIDE_ENABLED` | enabled | +| `TRUTHGATE_KUBO_PROVIDE_STRATEGY` | `all` | +| `TRUTHGATE_KUBO_PROVIDE_INTERVAL` | `22h` | +| `TRUTHGATE_KUBO_PROVIDE_SWEEP` | enabled | +| `TRUTHGATE_KUBO_PROVIDE_RESUME` | enabled | +| `TRUTHGATE_KUBO_STORAGE_MAX` | `auto` | +| `TRUTHGATE_KUBO_STORAGE_PERCENT` | `90` | +| `TRUTHGATE_KUBO_STORAGE_FALLBACK` | `200GB` | +| `TRUTHGATE_KUBO_STORAGE_GC_WATERMARK` | `90` | +| `TRUTHGATE_KUBO_ENABLE_GC` | enabled | +| `TRUTHGATE_KUBO_PUBLIC_IPV4` | `auto` | +| `TRUTHGATE_KUBO_PUBLIC_IPV6` | `auto` | +| `TRUTHGATE_KUBO_ANNOUNCE_PORT` | `IPFS_SWARM_PORT` | + +Optional variables are passed as empty strings by Compose when unset. The appliance then uses persistent settings or defaults. diff --git a/docs/reference/image-tags.md b/docs/reference/image-tags.md new file mode 100644 index 0000000..125d474 --- /dev/null +++ b/docs/reference/image-tags.md @@ -0,0 +1,40 @@ +# Docker image tags + +Repository: + +```text +magiccodingman/truthgate-ipfs +``` + +## Moving tags + +| Tag | Meaning | +|---|---| +| `latest` | newest successful stable release; Docker default | +| `stable` | explicit newest successful stable release | +| `0.1` | newest successful release in the `0.1.x` series | + +## Immutable identifiers + +| Tag | Meaning | +|---|---| +| `0.1.0` | exact semantic release | +| `sha-` | image built from a specific commit | + +## Development channel + +The rolling master image is published to GitHub Container Registry: + +```text +ghcr.io/magiccodingman/truthgate-ipfs:master +``` + +It is not the default production image. + +## Pull + +```bash +docker pull magiccodingman/truthgate-ipfs:stable +``` + +Omitting a tag pulls `latest`, which intentionally points to the same digest as `stable`. diff --git a/docs/reference/index.md b/docs/reference/index.md new file mode 100644 index 0000000..6fbf282 --- /dev/null +++ b/docs/reference/index.md @@ -0,0 +1,11 @@ +# Reference + +Exact values and operational contracts: + +- [Environment variables](environment-variables.md) +- [Ports and paths](ports-and-paths.md) +- [Kubo defaults](kubo-defaults.md) +- [Image tags](image-tags.md) +- [Rate limits](rate-limits.md) + +Reference pages should be updated with code changes that alter defaults. diff --git a/docs/reference/kubo-defaults.md b/docs/reference/kubo-defaults.md new file mode 100644 index 0000000..99b6863 --- /dev/null +++ b/docs/reference/kubo-defaults.md @@ -0,0 +1,27 @@ +# Kubo defaults + +TruthGate initializes and manages Kubo as a contributing server. + +| Setting | Default | +|---|---| +| `Routing.Type` | `dhtserver` | +| `Swarm.EnableHolePunching` | `true` | +| `Swarm.RelayClient.Enabled` | `true` | +| `Provide.Enabled` | `true` | +| `Provide.Strategy` | `all` | +| `Provide.DHT.Interval` | `22h` | +| `Provide.DHT.SweepEnabled` | `true` | +| `Provide.DHT.ResumeEnabled` | `true` | +| `Datastore.StorageMax` | auto, 90% of blockstore filesystem | +| `Datastore.StorageGCWatermark` | `90` | +| Automatic repository GC | enabled | +| `Addresses.API` | loopback `5001` | +| `Addresses.Gateway` | loopback `9010` | + +TruthGate preserves the current Kubo swarm-listener array and appends missing standard listeners instead of replacing the array. This reduces the chance of deleting transports added by a newer Kubo default. + +Inspect the effective node: + +```bash +docker exec truthgate truthgate-kubo-status +``` diff --git a/docs/reference/ports-and-paths.md b/docs/reference/ports-and-paths.md new file mode 100644 index 0000000..1e7b463 --- /dev/null +++ b/docs/reference/ports-and-paths.md @@ -0,0 +1,46 @@ +# Ports and paths + +## Host ports + +| Host | Container | Protocol | Purpose | +|---:|---:|---|---| +| `80` | `80` | TCP | HTTP and ACME | +| `443` | `443` | TCP | HTTPS | +| `4001` | `4001` | TCP | IPFS swarm | +| `4001` | `4001` | UDP | QUIC/WebTransport swarm | +| `8080` dev | `80` | TCP | Development portal | + +## Internal listeners + +| Listener | Address | +|---|---| +| Kubo RPC | `/ip4/127.0.0.1/tcp/5001` | +| Kubo gateway | `/ip4/127.0.0.1/tcp/9010` | + +## Container paths + +| Path | Purpose | +|---|---| +| `/app` | Published TruthGate application | +| `/workspace` | Development source mount | +| `/data/truthgate/config/config.json` | TruthGate configuration | +| `/data/truthgate/config/kubo-settings.json` | Managed Kubo settings | +| `/data/truthgate/config/kubo-overrides.json` | Advanced Kubo overrides | +| `/data/truthgate/database` | Databases | +| `/data/truthgate/certificates` | Certificates and ACME state | +| `/data/truthgate/state` | Bootstrap and migration state | +| `/data/truthgate/secrets/data-protection-keys` | ASP.NET data-protection keys | +| `/data/ipfs/repo` | Kubo repository | +| `/data/ipfs/repo/blocks` | Kubo blocks | +| `/run/truthgate` | Runtime temporary state | + +## Managed MFS paths + +TruthGate publishing uses managed MFS areas including: + +```text +/production/ +/staging/ +``` + +Do not manually reorganize managed publishing paths without understanding the publishing implementation. diff --git a/docs/reference/rate-limits.md b/docs/reference/rate-limits.md new file mode 100644 index 0000000..d5d52ec --- /dev/null +++ b/docs/reference/rate-limits.md @@ -0,0 +1,33 @@ +# Rate-limit defaults + +These values describe current application defaults. Operators and future releases may change them. + +## Public endpoints + +| Previous-hour global traffic | Per-IP budget | +|---:|---:| +| less than `2,000` | `300/min` | +| at least `2,000` | `200/min` | +| at least `8,000` | `100/min` | +| at least `16,000` | `30/min` | + +## Gateway routes + +| Setting | Default | +|---|---:| +| free per minute per IP | `400` | +| sliding hourly overage | `3,200` | +| ban when overage is exhausted | `4 hours` | +| authenticated auto-whitelist | enabled | + +## Response model + +- throttled request: normally `429`; +- banned client: `403`; +- invalid or missing key: `401`. + +Responses should not disclose remaining quota, ban duration, or whether a supplied key exists. + +## Operational warning + +Rate limits protect application resources. They are not sufficient for volumetric DDoS defense. diff --git a/docs/setup/configuration.md b/docs/setup/configuration.md new file mode 100644 index 0000000..24684b7 --- /dev/null +++ b/docs/setup/configuration.md @@ -0,0 +1,69 @@ +# Configuration + +TruthGate configuration has three layers. + +## 1. Compose and `.env` + +Host-facing values belong in `.env`: + +- image tag; +- host ports; +- persistent host paths; +- build versions; +- certificate mode; +- selected Kubo overrides. + +See [Environment variables](../reference/environment-variables.md). + +## 2. Persistent Kubo settings + +On first boot TruthGate creates: + +```text +data/truthgate/config/kubo-settings.json +``` + +This stores the managed Kubo policy. Environment variables take precedence over it. + +Changing the file requires a container restart for current settings to be reapplied. + +## 3. Advanced Kubo overrides + +Advanced values can be placed in: + +```text +data/truthgate/config/kubo-overrides.json +``` + +Example: + +```json +{ + "Swarm.ConnMgr.LowWater": 100, + "Swarm.ConnMgr.HighWater": 300, + "Routing.AcceleratedDHTClient": false +} +``` + +Overrides run after the normal managed defaults. + +`Addresses.API` and `Addresses.Gateway` are protected and cannot be changed through this file. TruthGate relies on those listeners remaining loopback-only. + +## Empty environment values + +The Compose file passes optional variables as empty strings when they are unset. Startup logic treats an empty value as “use the persistent setting or default,” not as a literal Kubo value. + +## Certificate settings + +```env +TRUTHGATE_ACME_STAGING=false +TRUTHGATE_CERT_IPS= +``` + +Use ACME staging for repeated certificate testing. + +`TRUTHGATE_CERT_IPS` is a comma-separated override for the IP addresses included in the self-signed fallback certificate. + +## Configuration ownership + +Do not edit generated application configuration while TruthGate is actively writing it. Back up the file first and stop the container for manual recovery work. diff --git a/docs/setup/docker.md b/docs/setup/docker.md new file mode 100644 index 0000000..e66d596 --- /dev/null +++ b/docs/setup/docker.md @@ -0,0 +1,82 @@ +# Docker installation + +TruthGate ships as one appliance containing TruthGate, Kubo, and the matching `ipfs` CLI. + +## Requirements + +- Docker Engine +- Docker Compose v2.24.4 or newer +- a Linux host for the documented production path +- TCP `80`, `443`, and the selected swarm port +- UDP on the selected swarm port + +## Install + +```bash +git clone https://github.com/magiccodingman/TruthGate-IPFS.git +cd TruthGate-IPFS +cp .env.example .env +``` + +Review `.env`, especially the persistent host paths. + +Pull and start: + +```bash +docker compose pull +docker compose up -d +``` + +Follow startup: + +```bash +docker compose logs -f truthgate +``` + +## Why the repository is cloned + +The published container image contains the software. The repository supplies the supported Compose definition, example environment file, documentation, and local build context. + +## Production image + +By default: + +```text +magiccodingman/truthgate-ipfs:stable +``` + +Use an exact version for a pinned deployment: + +```env +TRUTHGATE_IMAGE=magiccodingman/truthgate-ipfs:0.1.0 +``` + +## Local production build + +```bash +docker compose up --build -d +``` + +This is useful when testing local source changes. Normal operators should pull a tested release. + +## Verify + +```bash +docker compose ps +docker exec truthgate truthgate-kubo-status +``` + +The container should report healthy after Kubo and TruthGate are ready. + +## Security properties of the Compose definition + +The production service: + +- uses a read-only root filesystem; +- enables `no-new-privileges`; +- runs managed processes as the non-root `truthgate` user; +- places temporary data in bounded `tmpfs` mounts; +- binds Kubo RPC and gateway services to loopback; +- persists application and repository data outside the image. + +Docker is a packaging and isolation layer, not a complete host-security policy. Keep Docker and the host patched, restrict administrative access, and back up persistent data. diff --git a/docs/setup/first-run.md b/docs/setup/first-run.md new file mode 100644 index 0000000..6f2fb13 --- /dev/null +++ b/docs/setup/first-run.md @@ -0,0 +1,73 @@ +# First run + +![TruthGate login](../../content/images/demo/login.webp) + +## Generated administrator credentials + +On the first boot, TruthGate creates: + +```text +Username: admin +Password: a unique generated value +``` + +Retrieve it from logs: + +```bash +docker compose logs truthgate +``` + +Look for: + +```text +First-run administrator account: admin +First-run administrator password: ... +``` + +The password is also retained temporarily at: + +```text +data/truthgate/state/bootstrap-admin-password +``` + +That file uses restrictive permissions and is removed after the persistent TruthGate configuration exists. + +## Open the portal + +For an initial IP-only deployment: + +```text +https://YOUR_SERVER_IP +``` + +The first connection uses a self-signed fallback certificate. Browser warnings are expected until a configured domain receives a trusted certificate. + +## Required first actions + +1. Sign in as `admin`. +2. Change the administrator password. +3. Create a second administrative account when appropriate. +4. Verify Kubo status. +5. Review storage settings. +6. Confirm external swarm reachability. +7. Back up the persistent paths. + +## Native IPFS WebUI + +Open the WebUI from the authenticated TruthGate navigation. It is proxied through TruthGate; do not expose Kubo's local WebUI or RPC listener directly. + +## Verify Kubo + +```bash +docker exec truthgate truthgate-kubo-status +``` + +From another node: + +```bash +ipfs routing findpeer +``` + +## Generated password override + +Automated test environments may provide `TRUTHGATE_BOOTSTRAP_ADMIN_PASSWORD` directly to the container. Normal deployments should use the generated password rather than committing a shared credential to `.env`. diff --git a/docs/setup/index.md b/docs/setup/index.md new file mode 100644 index 0000000..ac63db3 --- /dev/null +++ b/docs/setup/index.md @@ -0,0 +1,35 @@ +# Setup and operations + +Production deployment is Docker-first. + +## Guides + +- [Docker installation](docker.md) +- [First run](first-run.md) +- [Networking](networking.md) +- [Configuration](configuration.md) +- [Storage and backups](storage-and-backups.md) +- [Updating](updating.md) +- [Migrating from legacy installations](migrating-from-legacy.md) + +## Recommended sequence + +1. Install Docker and the Compose plugin. +2. Clone the repository. +3. Copy `.env.example` to `.env`. +4. Choose persistent host paths. +5. Open HTTP, HTTPS, and swarm ports. +6. Start the stable image. +7. Retrieve the generated administrator password. +8. Change the password. +9. Verify Kubo status from inside and outside the host. +10. Configure domains, pins, and publishing. + +## Supported image architectures + +The stable Docker Hub release is published for: + +- `linux/amd64` +- `linux/arm64` + +The same image tag selects the correct architecture automatically. diff --git a/docs/setup/migrating-from-legacy.md b/docs/setup/migrating-from-legacy.md new file mode 100644 index 0000000..3407973 --- /dev/null +++ b/docs/setup/migrating-from-legacy.md @@ -0,0 +1,85 @@ +# Migrating from a legacy installation + +This guide covers moving a previous bare-metal or separate-Kubo deployment into the current appliance. + +## Do not initialize over existing data + +The important inputs are: + +- TruthGate configuration and database; +- certificates and secrets; +- Kubo repository; +- Kubo blocks; +- IPNS keys. + +Back up the legacy system before copying anything. + +## Stop legacy services + +Stop the old TruthGate service and Kubo daemon so files stop changing. + +Examples may include: + +```bash +sudo systemctl stop truthgate +sudo systemctl stop ipfs +``` + +Use the actual service names on the old host. + +## Prepare appliance paths + +```bash +mkdir -p \ + data/truthgate \ + data/ipfs/repo \ + data/ipfs/blocks +``` + +Copy the existing data into the corresponding paths. + +The Kubo repository must still see its blockstore at `repo/blocks`; the Compose definition mounts the separate host block directory at that location. + +## Repository migration + +Startup reads the existing repository version. When it is older than the format required by the bundled Kubo, TruthGate runs the repository migrator before applying managed configuration. + +If the repository version is newer than the bundled Kubo supports, startup stops instead of attempting a downgrade. + +## Managed settings + +The appliance applies its server defaults after migration. Review: + +- `data/truthgate/config/kubo-settings.json` +- `data/truthgate/config/kubo-overrides.json` +- `.env` + +The server profile is tracked so it is not blindly treated as a new repository on every start. + +## Identity validation + +Before migration: + +```bash +ipfs id -f='\n' +``` + +After migration: + +```bash +docker exec truthgate ipfs id -f='\n' +``` + +The peer ID should match when the original repository and identity were preserved. + +## Remove obsolete deployment pieces + +After validation, retire: + +- the old systemd TruthGate service; +- the old Kubo daemon service or separate container; +- direct public bindings for Kubo RPC and gateway ports; +- shared default credentials; +- auto-update scripts that pull source at service start. + +Do not remove backups until the appliance has passed external retrieval, domain, IPNS, and login tests. diff --git a/docs/setup/networking.md b/docs/setup/networking.md new file mode 100644 index 0000000..2dfaa9d --- /dev/null +++ b/docs/setup/networking.md @@ -0,0 +1,82 @@ +# Networking + +## Published ports + +| Purpose | Protocol | Default host port | Container port | +|---|---:|---:|---:| +| ACME HTTP and optional redirects | TCP | `80` | `80` | +| HTTPS portal and sites | TCP | `443` | `443` | +| Kubo swarm | TCP | `4001` | `4001` | +| Kubo QUIC/WebTransport swarm | UDP | `4001` | `4001` | + +Development replaces the HTTP/HTTPS publishing with host port `8080` to container port `80`, while retaining swarm TCP and UDP. + +## Firewalls and provider rules + +Allow: + +```text +80/tcp +443/tcp +4001/tcp +4001/udp +``` + +Replace `4001` when `IPFS_SWARM_PORT` is customized. + +## NAT + +When the host is behind NAT, forward both TCP and UDP for the selected swarm port. Forward HTTP and HTTPS when the portal or mapped domains must be reachable publicly. + +## Public announce addresses + +TruthGate can discover public IPv4 and IPv6 addresses and manage Kubo `Addresses.AppendAnnounce`. + +```env +TRUTHGATE_KUBO_PUBLIC_IPV4=auto +TRUTHGATE_KUBO_PUBLIC_IPV6=auto +TRUTHGATE_KUBO_ANNOUNCE_PORT=4001 +``` + +Use `off` to disable one family or set a literal address when auto-detection is inappropriate. + +## Internal Kubo listeners + +Inside the container: + +```text +RPC API: /ip4/127.0.0.1/tcp/5001 +Gateway: /ip4/127.0.0.1/tcp/9010 +``` + +These are intentionally not published by Compose. + +## Reverse proxies + +TruthGate is designed to terminate TLS itself. An upstream proxy is possible, but it must preserve the original host and protocol and correctly forward ACME traffic. + +Do not add another proxy merely because the application uses Kestrel. Kestrel is the intended public server in the standard deployment. + +## Validate a mapped host locally + +```bash +curl -kI \ + --resolve example.com:443:127.0.0.1 \ + https://example.com/ +``` + +## Validate swarm reachability + +Inside: + +```bash +docker exec truthgate ipfs id +``` + +Outside, from another node: + +```bash +ipfs routing findpeer +``` + +An address appearing in local configuration does not prove that the internet can reach it. diff --git a/docs/setup/storage-and-backups.md b/docs/setup/storage-and-backups.md new file mode 100644 index 0000000..1c5f376 --- /dev/null +++ b/docs/setup/storage-and-backups.md @@ -0,0 +1,94 @@ +# Storage and backups + +## Persistent host layout + +Default: + +```text +data/ +├── truthgate/ +│ ├── certificates/ +│ ├── config/ +│ ├── database/ +│ ├── secrets/ +│ └── state/ +└── ipfs/ + ├── repo/ + └── blocks/ +``` + +The mounts are: + +| Host path | Container path | +|---|---| +| `TRUTHGATE_DATA_HOST_PATH` | `/data/truthgate` | +| `IPFS_REPO_HOST_PATH` | `/data/ipfs/repo` | +| `IPFS_BLOCKS_HOST_PATH` | `/data/ipfs/repo/blocks` | + +The separate blockstore mount allows large block storage to move independently without changing Kubo's logical repository layout. + +## What must be backed up + +Back up all three persistent paths together. + +Important state includes: + +- TruthGate configuration and database; +- user and API-key records; +- certificates and ACME account state; +- ASP.NET data-protection keys; +- Kubo identity and configuration; +- IPNS keys; +- repository metadata; +- blocks; +- bootstrap and migration state. + +Backing up only the blockstore is not a complete backup. + +## Consistent backup + +For the simplest consistent snapshot: + +```bash +docker compose stop truthgate +``` + +Take the filesystem or volume snapshot, then: + +```bash +docker compose start truthgate +``` + +Storage systems with application-consistent snapshots may support more advanced approaches, but they must preserve the relationship among application state, repository metadata, and blocks. + +## Restore + +1. Stop TruthGate. +2. Restore all persistent paths. +3. Confirm ownership is compatible with the configured UID and GID. +4. Start the container. +5. Inspect migration and startup logs. +6. Verify the peer ID and published content. + +## Storage policy + +Default Kubo storage is `auto`, calculated as a percentage of the filesystem containing the blockstore. + +```env +TRUTHGATE_KUBO_STORAGE_MAX=auto +TRUTHGATE_KUBO_STORAGE_PERCENT=90 +TRUTHGATE_KUBO_STORAGE_FALLBACK=200GB +TRUTHGATE_KUBO_STORAGE_GC_WATERMARK=90 +TRUTHGATE_KUBO_ENABLE_GC=true +``` + +`Datastore.StorageMax` is a garbage-collection policy threshold, not a filesystem quota. Leave headroom for repository metadata, temporary files, and other host workloads. + +## Moving the blockstore + +1. Stop the container. +2. Copy the current blocks directory while preserving contents. +3. Change `IPFS_BLOCKS_HOST_PATH`. +4. Start the container. +5. Verify repository statistics and sample content. +6. Remove the old copy only after validation. diff --git a/docs/setup/updating.md b/docs/setup/updating.md new file mode 100644 index 0000000..565ff84 --- /dev/null +++ b/docs/setup/updating.md @@ -0,0 +1,75 @@ +# Updating and image tags + +## Update a stable deployment + +```bash +docker compose pull +docker compose up -d +``` + +Mounted state remains in place while the container is replaced. + +## Published tags + +Moving channels: + +```text +latest +stable +0.1 +``` + +Immutable release identifiers: + +```text +0.1.0 +0.1.1 +sha- +``` + +`latest` and `stable` point to the newest successful stable release. The series tag points to the newest release in that major/minor line. + +## Pin an exact release + +```env +TRUTHGATE_IMAGE=magiccodingman/truthgate-ipfs:0.1.0 +``` + +This avoids automatic movement when `stable` is updated. + +## Automatic version allocation + +A successful promotion to the `stable` Git branch: + +1. runs the release gates; +2. allocates or reuses a Git tag such as `v0.1.0`; +3. publishes AMD64 and ARM64 manifests; +4. updates version, series, `stable`, `latest`, and SHA tags; +5. verifies the remote manifest; +6. synchronizes the root README to Docker Hub. + +`VERSION` contains the intentional major/minor series. Patch numbers increment automatically. + +## Before updating + +- review release notes and the PR; +- back up persistent state; +- record the current image tag; +- check available disk space. + +## Roll back + +Set the old exact image tag in `.env`: + +```env +TRUTHGATE_IMAGE=magiccodingman/truthgate-ipfs:0.1.0 +``` + +Then: + +```bash +docker compose pull +docker compose up -d +``` + +A software rollback cannot always reverse a Kubo repository migration. Keep a pre-update backup when crossing Kubo repository versions. diff --git a/docs/site-publishing.md b/docs/site-publishing.md new file mode 100644 index 0000000..9bbc87c --- /dev/null +++ b/docs/site-publishing.md @@ -0,0 +1,136 @@ +# Site publishing + +TruthGate turns a static build directory into an IPFS-hosted site that can be reached by CID, optionally updated through IPNS/TGP, and served through one or more HTTPS domains. + +![Publishing a mapped domain](../content/images/demo/domains-publish-dark.webp) + +## What can be published + +TruthGate is designed for files that can be served without an application server: + +- HTML, CSS, and JavaScript +- images, fonts, and downloadable assets +- WebAssembly +- Blazor WebAssembly output +- React, Vue, Svelte, and other SPA production builds +- static documentation sites +- pre-rendered application output + +A server-rendered application that requires its own backend process is not a static site merely because it has a frontend. + +## Publishing model + +A publication connects four layers: + +1. **Site files** — the current static build. +2. **Site CID** — the immutable IPFS root produced from those files. +3. **Mutable identity** — optional IPNS and TGP metadata pointing to the current CID. +4. **Domain mapping** — optional HTTP routing and certificate configuration. + +The CID remains independently usable even when a domain or IPNS identity is added. + +## Basic workflow + +1. Build the site for production. +2. Confirm that the build output uses relative or deployment-compatible paths. +3. Open the Domains area in TruthGate. +4. Create or select the domain configuration. +5. Upload or publish the build output. +6. Wait for the publication job to finish. +7. Record the resulting CID. +8. Verify the CID directly. +9. If configured, verify IPNS/TGP. +10. Verify the HTTPS domain. + +## Domain behavior + +A mapped domain is not the management portal. Requests for that host are routed to the site's published IPFS content. + +An IP address or unmapped management host serves the authenticated TruthGate application. + +Read [Request routing](concepts/request-routing.md) before adding unusual redirects, wildcard mappings, or multiple project hosts. + +## TLS + +TruthGate uses a self-signed fallback certificate for IP addresses and unknown hosts. Configured domains can receive ACME certificates. + +For issuance to succeed: + +- DNS must point to the server. +- Public TCP port `80` must reach TruthGate for HTTP-01 challenges. +- Public TCP port `443` must reach TruthGate for HTTPS. +- Another reverse proxy must not consume the challenge unless it forwards it correctly. + +Use ACME staging while testing repeated certificate changes. + +## SPA routing + +A single-page application often expects unknown paths to return `index.html`. TruthGate's mapped-domain pipeline supports SPA-oriented fallback behavior. + +That fallback should not hide genuinely missing assets. Verify deep links and direct asset URLs separately. + +## CID verification + +Always verify the immutable output before debugging a domain: + +```text +https://YOUR_TRUTHGATE_HOST/ipfs// +``` + +An authenticated gateway request may require a login session or API key depending on the route and current configuration. + +From another IPFS node, a stronger validation is: + +```bash +ipfs cat /ipfs//index.html +``` + +This distinguishes publication problems from DNS, TLS, or domain-routing problems. + +## IPNS and watched pins + +IPNS gives a stable identity whose target can change. TruthGate can also watch an IPNS name and keep its current target pinned locally. + +Publishing to IPNS and subscribing to IPNS are related but distinct operations: + +- **Publishing** updates an identity you control. +- **Watching** resolves an identity and maintains its targets locally. + +See [Pinning, IPNS, and publishing](concepts/pinning-ipns-and-publishing.md). + +## TGP publication + +A TGP publication points an IPNS identity at a tiny directory containing `tgp.json`, optional browser fallback content, and an optional legal notice. The `current` field identifies the active site CID. + +Read the complete [TGP publisher workflow](tgp/publisher-workflow.md). Do not interpret TGP as a mechanism for deleting copies held by other nodes. + +## Public metadata + +A published domain can expose its current CID and IPNS/TGP state through read-only metadata endpoints. These are useful for: + +- deployment verification; +- freshness checks; +- monitoring; +- client-side discovery; +- cross-node pinning automation. + +See [Domain metadata API](api/domain-metadata.md). + +## Compression + +Do not assume that compiler-generated `.br` or `.gz` files should always be removed. Compression behavior depends on the current publishing and serving implementation and on how the application references those files. + +Before documenting a framework-specific rule, test the exact production output through both the mapped HTTPS domain and an IPFS path. The repository documentation intentionally avoids the old universal claim that all precompressed output is unusable on IPFS. + +## Publication checklist + +- [ ] The build output is static. +- [ ] `index.html` exists at the intended root. +- [ ] Direct CID access works. +- [ ] Deep SPA routes work. +- [ ] Required assets return successful responses. +- [ ] Domain DNS reaches the correct host. +- [ ] Certificate issuance succeeds. +- [ ] IPNS resolves to the expected value, when used. +- [ ] `tgp.json` validates, when used. +- [ ] Public metadata reports the intended CID. diff --git a/docs/tgp/client-resolution.md b/docs/tgp/client-resolution.md new file mode 100644 index 0000000..2dee8d2 --- /dev/null +++ b/docs/tgp/client-resolution.md @@ -0,0 +1,60 @@ +# TGP client resolution + +## Minimal algorithm + +```javascript +async function resolveTgp(baseUrl) { + const response = await fetch(new URL("tgp.json", baseUrl), { + cache: "no-store" + }); + + if (!response.ok) { + throw new Error(`TGP fetch failed: ${response.status}`); + } + + const pointer = await response.json(); + + if (pointer?.tgp !== 1) { + throw new Error("Unsupported TGP version"); + } + + if (typeof pointer.current !== "string" || pointer.current.length === 0) { + throw new Error("Missing TGP current target"); + } + + if (pointer.current.startsWith("/ipfs/")) { + return pointer.current; + } + + return `/ipfs/${pointer.current}`; +} +``` + +Production clients should also validate the CID/path and handle IPNS/gateway trust explicitly. + +## Browser redirect + +A browser helper can use `location.replace` after validating the pointer. + +Avoid open redirects. TGP v1 `current` is an IPFS target, not an arbitrary URL. + +## Last-known-good behavior + +A client MAY retain a previously validated target. When the current pointer cannot be fetched: + +- do not silently claim the old target is current; +- expose stale state where the UI permits; +- apply a bounded retention policy; +- retry with backoff. + +## Trust + +TGP inherits the trust model of the IPNS identity and the resolution path. + +A client using a public HTTP gateway also trusts that gateway to return the resolved content correctly. A local IPFS node reduces reliance on that HTTP gateway but does not change the identity being resolved. + +## Caching + +Use short caching for `tgp.json`. Cache the immutable target normally. + +A client that polls should use conditional requests or a reasonable interval rather than creating unnecessary load. diff --git a/docs/tgp/faq.md b/docs/tgp/faq.md new file mode 100644 index 0000000..510d91f --- /dev/null +++ b/docs/tgp/faq.md @@ -0,0 +1,41 @@ +# TGP FAQ + +## Why not point IPNS directly at the full site? + +You can. TGP is useful when clients and gateways benefit from a tiny, explicit current-pointer document and a narrow mutable contract. + +## Does TGP delete old content? + +No. It supports updating the pointer, unpinning locally, and garbage collecting local blocks. Other nodes may retain copies. + +## Is `tgp` version `1` or `1.1`? + +The wire value is integer `1`. Documentation revisions do not change the JSON wire version. + +## Does `tgp.json` need its own signature? + +IPNS already authenticates publication through its signed record. TGP v1 does not add a second signature field. + +## Can I include extra fields? + +Yes. V1 clients must ignore unknown fields. + +## Can `current` be an HTTP URL? + +No. V1 defines a CID or `/ipfs/` path. Allowing arbitrary URLs would create redirect and trust problems. + +## Is `index.html` required? + +No. It is an optional browser helper. + +## Is `legal.md` required? + +No. It is optional and informational. Its presence must not block resolution. + +## Can I keep deployment history? + +Yes, but TGP does not define a public history list. Keep audit history outside the pointer. + +## Does it work with IPFS Companion? + +A browser redirect to `/ipfs/` can be handled by an IPFS-aware browser environment or ordinary gateway routing. diff --git a/docs/tgp/gateway-behavior.md b/docs/tgp/gateway-behavior.md new file mode 100644 index 0000000..118fb78 --- /dev/null +++ b/docs/tgp/gateway-behavior.md @@ -0,0 +1,49 @@ +# TGP gateway behavior + +A gateway that understands TGP can enforce a narrow host-level contract. + +## Recommended behavior + +For a TGP-backed IPNS host: + +1. resolve and validate `tgp.json`; +2. identify the current target; +3. serve or redirect to that target; +4. use short caching for the pointer; +5. use normal immutable caching for the target; +6. expose the optional legal notice; +7. reject malformed pointers rather than guessing. + +## Current-target restriction + +A gateway MAY restrict the TGP host to the CID advertised by `current`. + +This prevents the same host from becoming a general-purpose path to unrelated or previously advertised CIDs. + +It does not prevent users from fetching a known old CID through another gateway or node. + +## Browser fallback + +An optional `index.html` can fetch `tgp.json` and redirect. + +The fallback should: + +- avoid arbitrary URL redirects; +- identify stale fallback behavior; +- preserve subpaths only when deliberately specified; +- fail clearly when the pointer is invalid. + +## Caches + +A reverse proxy SHOULD avoid serving a stale pointer for long periods. Immutable site files can receive long-lived caching. + +## Errors + +Useful gateway failures include: + +- unsupported TGP version; +- malformed pointer; +- current target unavailable; +- IPNS resolution failure. + +Do not include private configuration paths or keys in error responses. diff --git a/docs/tgp/index.md b/docs/tgp/index.md new file mode 100644 index 0000000..6704863 --- /dev/null +++ b/docs/tgp/index.md @@ -0,0 +1,58 @@ +# TruthGate Pointer protocol + +The **TruthGate Pointer protocol (TGP)** is a small convention for using an IPNS identity to advertise one current immutable IPFS target. + +An IPNS root contains a required `tgp.json` file. Clients read the pointer and then load the CID in `current`. + +```json +{ + "tgp": 1, + "ts": "2026-07-25T17:00:00Z", + "current": "bafy...", + "domainName": "example.com", + "legal": "/legal.md" +} +``` + +## Why it matters + +TGP keeps the mutable object tiny while the application remains immutable and cacheable. + +It supports: + +- inexpensive freshness checks; +- one current target; +- simple client resolution; +- ordinary caching of the immutable CID; +- unpin and garbage-collection workflows; +- gateway policies that only serve the advertised target; +- clear separation between current deployment and off-pointer audit history. + +## What it does not do + +TGP cannot: + +- erase blocks held by another node; +- revoke a CID globally; +- prevent caches, mirrors, archives, screenshots, or copies; +- guarantee that deleted content becomes undiscoverable; +- provide legal immunity; +- replace legal advice or an operator's compliance obligations. + +## Documentation + +- [Rationale](rationale.md) +- [Specification v1](specification-v1.md) +- [Client resolution](client-resolution.md) +- [Publisher workflow](publisher-workflow.md) +- [Gateway behavior](gateway-behavior.md) +- [Legal considerations](legal-considerations.md) +- [Legal-notice template](legal-notice-template.md) +- [FAQ](faq.md) + +## Version terminology + +- **Wire protocol version:** `1`, represented by `"tgp": 1`. +- **Specification revision:** editorial revision of these documents. + +A documentation clarification does not require clients to emit `tgp: 1.1`. A future incompatible wire protocol would use a new integer. diff --git a/docs/tgp/legal-considerations.md b/docs/tgp/legal-considerations.md new file mode 100644 index 0000000..f821926 --- /dev/null +++ b/docs/tgp/legal-considerations.md @@ -0,0 +1,53 @@ +# TGP legal considerations + +This page is informational and is not legal advice. + +## Operational purpose + +TGP gives an operator a clear current pointer and a practical workflow for: + +- updating the advertised target; +- avoiding a public history catalog in the pointer; +- unpinning removed content locally; +- running local garbage collection; +- documenting the current deployment; +- responding to moderation or takedown decisions. + +## Limits + +TGP cannot: + +- delete copies retained by another IPFS node; +- prevent third-party pinning; +- control public gateways, mirrors, caches, archives, or screenshots; +- revoke knowledge of an old CID; +- guarantee compliance with any law or contract; +- determine who is legally responsible for content; +- make an operator immune from claims. + +## Operator responsibility + +Operators remain responsible for: + +- the content they publish or intentionally retain; +- access-control choices; +- notices and contact procedures; +- applicable moderation and takedown processes; +- record retention; +- legal obligations in relevant jurisdictions. + +Consult qualified counsel for a production legal policy. + +## Claims documentation should avoid + +Do not describe TGP as: + +- legal immunity; +- guaranteed deletion; +- a right-to-erasure implementation by itself; +- proof that the operator never hosted content; +- protection from archives or independent peers. + +A more accurate statement is: + +> TGP can reduce the mutable and locally retained surface an operator intentionally maintains, while making the current target and removal workflow explicit. diff --git a/docs/tgp/legal-notice-template.md b/docs/tgp/legal-notice-template.md new file mode 100644 index 0000000..996feca --- /dev/null +++ b/docs/tgp/legal-notice-template.md @@ -0,0 +1,38 @@ +# Informational TGP notice template + +This is a technical starting point, not legal advice or a jurisdiction-specific policy. Have qualified counsel review any notice used in production. + +```markdown +# Current-content pointer notice + +This IPNS identity uses the TruthGate Pointer protocol (TGP) to identify one current IPFS content target. + +The current target is declared in `/tgp.json`. + +The operator may update that target, remove local pins, and run local repository garbage collection. These actions affect the operator's own systems. They do not delete or control copies that may have been retained by independent IPFS nodes, gateways, caches, mirrors, archives, or users. + +A CID is content-addressed and may remain retrievable from third parties after the operator stops advertising or retaining it. + +For questions or reports concerning the current target, contact: + +- Operator: [NAME] +- Contact: [CONTACT METHOD] +- Policy or jurisdiction information: [OPTIONAL] + +Last updated: [DATE] +``` + +## Usage + +1. Save the reviewed notice as `/legal.md` in the TGP root. +2. Add: + +```json +{ + "legal": "/legal.md" +} +``` + +to `tgp.json`. +3. Keep the notice contact information current. +4. Do not claim that the notice changes third-party behavior. diff --git a/docs/tgp/publisher-workflow.md b/docs/tgp/publisher-workflow.md new file mode 100644 index 0000000..d407c43 --- /dev/null +++ b/docs/tgp/publisher-workflow.md @@ -0,0 +1,69 @@ +# TGP publisher workflow + +## Inputs + +- an IPNS key controlled by the publisher; +- the current site root CID; +- optional domain name; +- optional legal notice. + +## Build the pointer directory + +```text +tgp-root/ +├── tgp.json +├── index.html optional +└── legal.md optional +``` + +Example `tgp.json`: + +```json +{ + "tgp": 1, + "ts": "2026-07-25T17:00:00Z", + "current": "bafy...", + "domainName": "example.com", + "legal": "/legal.md" +} +``` + +## Publish + +1. Validate the site CID. +2. Generate `tgp.json`. +3. Add the pointer directory to IPFS. +4. Publish the resulting pointer-directory CID through the IPNS key. +5. Verify IPNS resolution. +6. Fetch and validate `tgp.json`. +7. Fetch the current site CID. +8. Verify mapped-domain metadata. + +## Updating + +For a new deployment: + +1. publish the new site and obtain its CID; +2. update `current` and `ts`; +3. add the new pointer directory; +4. update IPNS; +5. wait for resolution; +6. verify clients; +7. apply old-target retention policy. + +## Removing old content locally + +When policy permits: + +```bash +ipfs pin rm +ipfs repo gc +``` + +Only run garbage collection with a complete understanding of other pins and repository users. + +This removes eligible blocks from the local node. It cannot remove blocks pinned or cached elsewhere. + +## Audit history + +Keep deployment history in an operator database, Git repository, logging system, or other off-pointer record. TGP does not define a public history array. diff --git a/docs/tgp/rationale.md b/docs/tgp/rationale.md new file mode 100644 index 0000000..1520c55 --- /dev/null +++ b/docs/tgp/rationale.md @@ -0,0 +1,54 @@ +# TGP rationale + +## The problem + +A static application is naturally represented by an immutable CID. Users and applications still need a stable name that can move to a new build. + +IPNS provides the signed mutable identity. A publisher can point that identity directly at the complete site, but clients that only need to discover the current deployment must resolve the entire mutable root conceptually as the application. + +TGP narrows that mutable surface. + +## The pointer model + +The IPNS identity points to a small directory containing: + +```text +/tgp.json +/index.html optional +/legal.md optional +``` + +`tgp.json` points to the current immutable site CID. + +## Operational benefits + +### Light mutable payload + +The pointer is small and cheap to fetch, validate, mirror, and monitor. + +### Immutable application caching + +After resolution, `/ipfs/` can use ordinary immutable-content caching. + +### Clear current target + +Clients do not need to infer which object in a directory is the deployment. + +### Removal workflow + +An operator can: + +1. update the pointer; +2. unpin an old target locally; +3. run repository garbage collection; +4. stop intentionally advertising that target through the IPNS identity. + +This controls the operator's own node and pointer. It does not control independent third-party copies. + +### No default history catalog + +TGP does not define a historical list. Audit records can exist elsewhere without becoming part of the public pointer contract. + +## Why IPNS still matters + +TGP does not replace IPNS signing, sequence handling, routing, or name resolution. It uses IPNS as the authenticated mutable identity and defines the payload clients expect after resolution. diff --git a/docs/tgp/specification-v1.md b/docs/tgp/specification-v1.md new file mode 100644 index 0000000..9298106 --- /dev/null +++ b/docs/tgp/specification-v1.md @@ -0,0 +1,186 @@ +# TruthGate Pointer protocol — wire version 1 + +**Status:** Open specification +**Wire version:** `1` + +The key words **MUST**, **MUST NOT**, **SHOULD**, **SHOULD NOT**, and **MAY** indicate conformance requirements. + +## 1. Root document + +A conforming TGP v1 IPNS root MUST provide: + +```text +/tgp.json +``` + +The path is relative to the root resolved by the IPNS identity. + +## 2. Media type and encoding + +`tgp.json` MUST be valid UTF-8 JSON. + +Servers SHOULD use: + +```text +Content-Type: application/json +``` + +## 3. Object schema + +Example: + +```json +{ + "tgp": 1, + "ts": "2026-07-25T17:00:00Z", + "current": "bafy...", + "domainName": "example.com", + "legal": "/legal.md" +} +``` + +### `tgp` + +- Type: integer +- Required: yes +- Value for this specification: `1` + +A client MUST reject an unsupported wire version. + +### `ts` + +- Type: string +- Required: yes +- Format: RFC 3339 / ISO 8601 UTC timestamp + +The timestamp indicates when the publisher generated the pointer. It is freshness metadata, not a replacement for IPNS sequence validation. + +### `current` + +- Type: string +- Required: yes + +Accepted forms: + +```text +bafy... +/ipfs/bafy... +``` + +The target MUST identify an IPFS content path. A v1 client MUST NOT invent a target when this field is absent or empty. + +### `domainName` + +- Type: string +- Required: no + +The primary associated DNS name. Clients MUST NOT treat this unsigned field separately from the trust provided by the resolved IPNS record. + +### `legal` + +- Type: string +- Required: no + +A path to an informational notice, normally: + +```text +/legal.md +``` + +The notice MUST NOT be required for pointer resolution. + +### Unknown fields + +Clients MUST ignore unknown fields so compatible metadata can be added without breaking v1 readers. + +## 4. Resolution + +A client MUST: + +1. resolve the intended IPNS identity; +2. fetch `/tgp.json`; +3. parse JSON; +4. verify `tgp` is supported; +5. verify `ts` and `current` exist; +6. normalize `current`; +7. load the IPFS target. + +Normalization: + +- if `current` begins with `/ipfs/`, use it; +- otherwise treat it as a CID and prepend `/ipfs/`. + +A client MUST NOT interpret arbitrary URL schemes from `current`. + +## 5. Caching + +The pointer is mutable and SHOULD use a short freshness lifetime. Sixty seconds is a reasonable default when no more specific policy exists. + +The immutable target MAY be cached according to normal IPFS and HTTP policy. + +A client MAY retain a last-known-good target for availability, but SHOULD indicate when it is using stale state. + +## 6. Failure behavior + +- missing document: fail or use an explicitly identified last-known-good target; +- malformed JSON: fail; +- unsupported version: fail; +- missing or empty `current`: fail; +- invalid target: fail; +- missing legal notice: continue resolution. + +Clients MUST NOT guess a CID from unrelated files. + +## 7. Optional files + +```text +/index.html +/legal.md +``` + +`index.html` MAY provide browser-oriented resolution. + +`legal.md` MAY provide operator information and limitations. + +## 8. JSON Schema + +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "TruthGate Pointer v1", + "type": "object", + "additionalProperties": true, + "required": ["tgp", "ts", "current"], + "properties": { + "tgp": { + "type": "integer", + "const": 1 + }, + "ts": { + "type": "string", + "format": "date-time" + }, + "current": { + "type": "string", + "minLength": 3 + }, + "domainName": { + "type": "string" + }, + "legal": { + "type": "string" + } + } +} +``` + +## 9. Conformance checklist + +- [ ] `/tgp.json` exists at the IPNS root. +- [ ] JSON is valid UTF-8. +- [ ] `tgp` is integer `1`. +- [ ] `ts` is a date-time string. +- [ ] `current` is a CID or `/ipfs/` path. +- [ ] unknown fields do not break clients. +- [ ] mutable pointer caching is short. +- [ ] clients fail safely on malformed data. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..230eda5 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,181 @@ +# Troubleshooting + +Start with the smallest layer that can fail. + +## Collect basic state + +```bash +docker compose ps +docker compose logs --tail=300 truthgate +docker inspect truthgate \ + --format 'Status={{.State.Status}} Health={{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}} RestartCount={{.RestartCount}}' +docker exec truthgate truthgate-kubo-status +``` + +## The portal loads but is not interactive + +Check the Blazor bootstrap asset: + +```bash +curl -skf \ + -H 'Accept-Encoding: identity' \ + https://127.0.0.1/_framework/blazor.web.js \ + -o /tmp/blazor.web.js + +wc -c /tmp/blazor.web.js +``` + +A zero-byte response indicates a broken publish. The production image build contains a regression guard for this asset. + +In a browser, inspect the Network panel for: + +- `/_framework/blazor.web.js` +- `/_blazor/negotiate` +- a persistent `/_blazor?id=...` connection + +## Login works but the page looks anonymous + +Perform a hard refresh after login. Confirm the authentication cookie is present and that the browser is using the same scheme, host, and port used during login. + +Do not add anonymous bypasses for `/_blazor` merely to hide an authentication problem. + +## Port conflicts during development + +Development publishes: + +- HTTP portal: host `8080` by default +- IPFS swarm TCP: host `4001` +- IPFS swarm UDP: host `4001` + +Stop another Kubo/IPFS Desktop node before starting the development appliance, or deliberately change `IPFS_SWARM_PORT`. + +```bash +ss -lntup | grep -E ':(4001|8080)\b' +``` + +## Container is unhealthy + +Inspect logs first. Common causes include: + +- persistent directories are not writable; +- a configured path is not absolute inside the container; +- an existing Kubo repository is newer than the bundled Kubo supports; +- another process owns a published host port; +- Kubo failed before its RPC API became ready; +- malformed Kubo override JSON; +- a certificate or configuration file is unreadable. + +## Domain serves the portal instead of the site + +Confirm: + +- the Host header matches the configured domain; +- the domain exists in TruthGate configuration; +- DNS points to this server; +- the domain spelling and case normalization are correct; +- the publication exists in the managed site path. + +Test the host locally: + +```bash +curl -kI --resolve example.com:443:127.0.0.1 https://example.com/ +``` + +## Domain serves the wrong site + +Check for: + +- wildcard mappings; +- multiple similar domain entries; +- stale publication configuration; +- uppercase/lowercase leaf differences in managed paths; +- redirects that change the host. + +## Certificate does not issue + +Verify: + +```bash +curl -I http://example.com/ +``` + +Port `80` must reach the same TruthGate instance. Confirm firewall rules, provider security groups, NAT, DNS, and any upstream proxy. + +Use ACME staging during repeated tests: + +```env +TRUTHGATE_ACME_STAGING=true +``` + +Do not leave staging enabled for a production certificate. + +## Node cannot be found externally + +Inside TruthGate: + +```bash +docker exec truthgate ipfs id +docker exec truthgate ipfs config --json Addresses.Swarm +docker exec truthgate ipfs config --json Addresses.AppendAnnounce +``` + +From a separate IPFS node and network: + +```bash +ipfs routing findpeer +``` + +Confirm TCP and UDP forwarding for the selected swarm port. + +## IPNS watch is stale + +Check: + +- the IPNS name resolves outside TruthGate; +- the watched entry is enabled; +- the background worker is running; +- the per-key cooldown has elapsed; +- the current CID is retrievable; +- TGP metadata is valid when TGP mode is enabled. + +IPNS publication and network resolution are not instantaneous. Test the current record independently before restarting services. + +## Data disappeared after recreation + +TruthGate state is only persistent when host paths or volumes remain mounted. + +Check `.env`: + +```env +TRUTHGATE_DATA_HOST_PATH=./data/truthgate +IPFS_REPO_HOST_PATH=./data/ipfs/repo +IPFS_BLOCKS_HOST_PATH=./data/ipfs/blocks +``` + +Do not use `docker compose down -v` when using named volumes that contain data. + +## Kubo repository migration fails + +Do not delete the repository version file or initialize over the existing directory. + +Back up all persistent paths, then inspect: + +```bash +cat data/ipfs/repo/version +docker compose logs truthgate +``` + +See [Migrating from legacy installations](setup/migrating-from-legacy.md). + +## Report an issue + +Include: + +- TruthGate image tag or commit; +- CPU architecture; +- Docker and Compose versions; +- relevant `.env` values with secrets removed; +- container health; +- logs around the failure; +- whether the problem occurs by IP, management domain, mapped domain, CID, or IPNS; +- exact reproduction steps. diff --git a/scripts/check-docs.py b/scripts/check-docs.py new file mode 100755 index 0000000..0d52ea1 --- /dev/null +++ b/scripts/check-docs.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +"""Validate TruthGate's repository documentation using only the standard library.""" + +from __future__ import annotations + +import re +import sys +from pathlib import Path +from urllib.parse import unquote + +REPO_ROOT = Path(__file__).resolve().parents[1] +DOCS_ROOT = REPO_ROOT / "docs" +MARKDOWN_FILES = [REPO_ROOT / "README.md", REPO_ROOT / "DOCKER.md", *DOCS_ROOT.rglob("*.md")] + +LINK_PATTERN = re.compile(r"(?= 0: + errors.append("README.md must link to docs/index.md") + +for markdown_file in MARKDOWN_FILES: + if not markdown_file.is_file(): + errors.append(f"missing Markdown file: {markdown_file.relative_to(REPO_ROOT)}") + continue + + content = markdown_file.read_text(encoding="utf-8") + + for stale_phrase in STALE_PHRASES: + if stale_phrase.casefold() in content.casefold(): + errors.append( + f"{markdown_file.relative_to(REPO_ROOT)} contains stale phrase: {stale_phrase}" + ) + + for match in LINK_PATTERN.finditer(content): + raw_target = next(group for group in match.groups() if group is not None).strip() + target = raw_target.split(maxsplit=1)[0].strip("<>") + target = unquote(target.split("#", 1)[0]) + + if not target or target.startswith(("http://", "https://", "mailto:")): + continue + + resolved = (markdown_file.parent / target).resolve() + try: + resolved.relative_to(REPO_ROOT.resolve()) + except ValueError: + errors.append( + f"{markdown_file.relative_to(REPO_ROOT)} links outside repository: {raw_target}" + ) + continue + + if not resolved.exists(): + errors.append( + f"{markdown_file.relative_to(REPO_ROOT)} has broken link: {raw_target}" + ) + +if errors: + print("Documentation validation failed:", file=sys.stderr) + for error in errors: + print(f"- {error}", file=sys.stderr) + raise SystemExit(1) + +print( + f"Documentation validation passed for {len(MARKDOWN_FILES)} Markdown files." +)