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.

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 two differences: the image goes to localhost:5000, this forge's staging registry (no login, no secret), and buildx's builder runs with network=host so it can reach that registry.
    name: image
    on:
      push:
        tags: ['v*']
    jobs:
      image:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - 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.

Who can publish: anyone who can push to a repository on this forge, since a build can push any image name to the staging registry. Images are public once on IPFS, even when they come from a private repository. Gitea holds a pull request from a fork until someone with write access approves its workflow run: read the workflow before you approve.

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 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)
Radicle explorer (public)
Radicle API and read-only git over HTTPS (public)
ipcr.localhost:4767IPCR registry, for this server's Docker only
TCP 8776 · TCP/UDP 4768Radicle peers · IPFS peers