IPCR Forge

From git push to docker pull on any server. Work in Gitea as usual: its Actions build your images, IPCR serves them from IPFS, and every public repository is mirrored to the Radicle peer-to-peer network.

GiteaYour repositories, pull requests, issues and builds. Sign in as gitea_admin. Published imagesWhat this forge serves from IPFS, with the line to put in a compose file. On RadicleThe public repositories mirrored to the Radicle network. Public. AdminYour ENS or DNS name, which repositories publish, unpublishing, the publisher key backup. Gitea site admins only.

How it works

1 · Pushgit push a v* tag to Gitea, or create it in Gitea's web UI
→
2 · BuildGitea Actions runs .gitea/workflows/ and pushes the image to localhost:5000
→
3 · IPFSIPCR imports each tag and publishes it under this forge's IPNS name
→
4 · Pullany server with IPCR: ipcr.localhost:4767/ipns/…

Steps 2 and 3 are automatic and usually take a few minutes. The very first build on this server takes longer (about 5 to 10 minutes) while it downloads its build images.

Alongside, every public repository is copied to Radicle within seconds of each push: branches and tags, signed by this server's Radicle node. Gitea stays the one place to work. The Radicle copy is for distribution: anyone can rad clone it, and it stays available even when this server is not. Private repositories are never copied.

Try it with the example

The install created gitea_admin/ipcr-hello, a tiny web page with a Dockerfile and the workflow that publishes it.

  1. Open it in Gitea, then Tags → New tag (or Releases → New release): name v1.0.0, target main.
  2. Follow the build under the repository's Actions tab.
  3. About a minute after it finishes, the image appears below under Published images, and the tag on Radicle.

Published images

Loading…

Publish your own project

  1. Create a repository in Gitea (+ → New repository) and push your code to it:
    git remote add origin https://gitea-<domain>/<owner>/<repo>.git
    git push -u origin main
    Git asks for your Gitea user name and password (or an access token, from Settings → Applications).
  2. Add a workflow at .gitea/workflows/build.yml. This one builds the Dockerfile at the root for every v* tag. It is a GitHub-style workflow with four differences: it runs on ipcr, this forge's own runner (the only one that reaches the staging registry), it logs in to localhost:5000, this forge's staging registry, as the repository itself (the forge sets the IPCR_PUSH_TOKEN secret on every public repository allowed to publish), the image goes there under the repository's own name, and buildx's builder runs with network=host so it can reach that registry.
    name: image
    on:
      push:
        tags: ['v*']
    jobs:
      image:
        runs-on: ipcr
        steps:
          - uses: actions/checkout@v4
          - uses: docker/login-action@v3
            with:
              registry: localhost:5000
              username: ${{ github.repository }}
              password: ${{ secrets.IPCR_PUSH_TOKEN }}
          - uses: docker/metadata-action@v5
            id: meta
            with:
              images: localhost:5000/${{ github.repository }}
              tags: |
                type=semver,pattern={{version}}
                type=raw,value=latest
          - uses: docker/setup-buildx-action@v3
            with:
              driver-opts: network=host
          - uses: docker/build-push-action@v6
            with:
              context: .
              push: true
              tags: ${{ steps.meta.outputs.tags }}
              labels: ${{ steps.meta.outputs.labels }}
  3. Release:
    git add .gitea && git commit -m "Build the image"
    git tag v1.0.0
    git push origin main v1.0.0
    The image is published as <owner>/<repo>:1.0.0 under this forge's name. Repositories of the forge's own organisation are also published at the root: <repo>:1.0.0.

Who can publish: each public repository publishes under its own name only, with its own push credential; the short root names belong to the forge's organisation, so to its members. Workflows from forks get no secrets, so they cannot publish. Private repositories never publish.

Use an image

Copy the line from Published images into a compose file. It works on this server and on any server running IPCR:

services:
  myapp:
    image: ipcr.localhost:4767/ipns/k51…/<owner>/<repo>:1.0.0   # a tag: follows new builds of 1.0.0
    # image: ipcr.localhost:4767/ipfs/bafkrei…   # a CID: this exact image, forever

Or pull it by hand: docker pull ipcr.localhost:4767/ipns/k51…/<owner>/<repo>:1.0.0. /ipns/… is a name this forge can move to a new build; /ipfs/<cid> is the image's content address and can never change.

For a readable name, point a name you own at this forge's publisher name (k51…), which holds every image it builds:

New releases need no change to either record: they update the publisher name.

On Radicle

Public repositories of this forge, as mirrored to the Radicle network by this server's node (…). Clone one with rad clone <id> from any Radicle node.

Loading…

A repository that turns private or is deleted in Gitea is no longer synced (frozen). What was already published stays on the Radicle network: it cannot be taken back.

Pull without IPCR

To try an image on a machine without IPCR, pull it through this forge's public front door, with plain Docker and nothing to install:

docker pull ipcr-<domain>/ipns/<name>/<repo>:1.0.0

It serves this forge's images only, read-only, and depends on this server being online. For anything that should keep working without it, use IPCR's local address (below): any IPCR node can serve the image.

Pull from another server

Any server with IPCR resolves the same lines and fetches the image from the IPFS network, from this server and from every other server that already pulled it.

On a Yundera server

Install IPCR from the App Store. There is nothing to configure.

On any Linux server with Docker 29+

mkdir -p /DATA/AppData/ipcr && cd /DATA/AppData/ipcr
curl -fsSLO https://raw.githubusercontent.com/Yundera/ipcr/main/Apps/IPCR/docker-compose.yml
install -d -m 0700 -o 1000 -g 1000 kubo && install -d -o 1000 -g 1000 init.d
curl -fsSL -o init.d/01-ipcr-node.sh https://raw.githubusercontent.com/Yundera/ipcr/main/Apps/IPCR/seed/init.d/01-ipcr-node.sh
chmod 0755 init.d/01-ipcr-node.sh
printf 'PUID=1000\nPGID=1000\nTZ=UTC\nAppID=ipcr\n' > .env
docker compose up -d

IPCR listens on 127.0.0.1:4767 only, and trusts itself through /etc/docker/certs.d/ipcr.localhost:4767/ca.crt. It publishes port 4768 (TCP and UDP) for IPFS peers. Details: github.com/Yundera/ipcr.

Addresses on this server

AddressWhat
Gitea: repositories, Actions, users (gitea_admin)
This page (public)
Admin pages: signed in with Gitea, site administrators only
Radicle explorer (public)
Radicle API and read-only git over HTTPS (public)
ipcr.localhost:4767IPCR registry, for this server's Docker only
Public front door: docker pull this forge's images from anywhere (read-only)
TCP 8776 · TCP/UDP 4768Radicle peers · IPFS peers