IPCR Forge

From git push to docker pull on any server, without a central service. Code lives on Radicle, this node builds it with GitHub-style workflows, and IPCR serves the image from IPFS.

RadicleBrowse repositories, issues and patches. Public. CI runsEvery build this node ran, with its log and result. Public. Published imagesWhat this server serves from IPFS, with the line to put in a compose file.

How it works

1 · Pushgit push rad a v* tag to the Radicle network
→
2 · Triggerthis node receives the tag and the Radicle CI broker picks it up
→
3 · Buildact runs the repo's .github/workflows/ and pushes the image
→
4 · IPFSIPCR imports each tag and publishes it under an IPNS name
→
5 · Pullany server with IPCR: ipcr.localhost:4767/ipns/…

Steps 2 to 4 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. A repository needs no configuration beyond committing a workflow. There is no second copy of it anywhere: the build reads the commit straight from this node. Builds of the default branch and of patches also write their result back into the repository, signed by the node, as a Radicle job.

Only changes signed by one of the repository's delegates are built: release tags, the default branch and their patches. A build can publish an image, so it gets the trust of a release.

Published images

Loading…

Publish a project

  1. Install Radicle on your machine and create an identity: see radicle.xyz, then rad auth. Start your own node, which carries your pushes: rad node start.
  2. Connect to this server's node so your pushes reach it:
    rad node connect <node-id>@<this server>:8776
  3. Publish the repository. It has to be a Git repository with at least one commit:
    git init -b main
    git add -A
    git commit -m "First commit"
    rad init
    rad init prints the repository ID (rad:z…).
  4. Let this node seed it. The node only keeps repositories it is told to (seeding policy block). In this server's Terminal app or over SSH:
    docker exec radicle-node rad seed rad:z… --scope all
    It ends with ✗ Target not met: could not fetch from […]. That is about the public Radicle seeds, not this server, and can be ignored. The repository is in place once it shows in the Radicle explorer.
  5. Add a workflow at .github/workflows/build.yml, as on GitHub. This one builds the Dockerfile at the root and publishes it for every v* tag. vars.IMAGE is set for every repository (this node's staging registry plus the repository's name); there is no secret to configure. Two lines differ from a GitHub-hosted workflow: context: . (builds the checkout, not a GitHub URL) and buildx's network=host (so its builder can reach the staging registry). Commit it (git add .github && git commit -m "Build the 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: ${{ vars.IMAGE }}
              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 }}
  6. Release:
    git tag -a v1.0.0 -m v1.0.0
    git push rad main v1.0.0
    Follow the build under CI runs. A minute after it finishes, the tag appears under Published images.

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/k51qzi5uqu5d…:1.0.0   # a tag: follows new builds of 1.0.0
    # image: ipcr.localhost:4767/ipfs/bafkrei…           # a CID: this exact image, forever

/ipns/<name>:<tag> is a name the publisher 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.

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