IPCR Forge: public front door (docker pull without IPCR)
ipcr-public: a second ipcrd behind Caddy at ipcr-<domain>, plain HTTP, no pinning, off the admin and CI networks. With PUBLIC_POLICY it serves only this forge's publisher name, its verified ENS/DNS name and its published image roots; everything else is 404, pushes 405. So docker pull ipcr-<domain>/ipns/metadec.eth/ipcr-hello:1.0.0 works on any machine with plain Docker. Switch in the admin page (config.json "public"), mirrored into published.json for the pages, which show the public pull lines while it is on. ipcr 1.5.0, bridge 0.4.0. Verified from a second machine (docs/forge.md).
This commit is contained in:
+30
-1
@@ -126,6 +126,16 @@
|
||||
<div class="copyrow"><code id="dns-value">dnslink=/ipns/k51…</code><button class="copy" data-copy="dns-value">Copy</button></div>
|
||||
</details>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<h3>Public pulls</h3>
|
||||
<p class="muted">This forge's images, and only those, can also be pulled from any machine with plain Docker, without IPCR,
|
||||
through <code id="public-host">ipcr-…</code>. Read-only; a pull there pins nothing. Layers are still checked by digest.</p>
|
||||
<form class="row" id="public-form">
|
||||
<label><input type="checkbox" id="public-on"> Serve public pulls</label>
|
||||
<span id="public-state" class="muted"></span>
|
||||
</form>
|
||||
<div class="copyrow"><code id="public-try">docker pull …</code><button class="copy" data-copy="public-try">Copy</button></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Health <button id="refresh">Check again</button> <span id="refresh-state" class="muted"></span></h2>
|
||||
@@ -216,6 +226,8 @@
|
||||
"use strict";
|
||||
var $ = function (id) { return document.getElementById(id); };
|
||||
var publisher = "", pubName = "";
|
||||
// The public front door's host: same suffix as this page, ipcr- instead of ipcr-forge-.
|
||||
var publicHost = location.hostname.indexOf("ipcr-forge-") === 0 ? "ipcr-" + location.hostname.slice("ipcr-forge-".length) : "";
|
||||
|
||||
function toast(msg, ms) {
|
||||
var t = $("toast"); t.textContent = msg; t.style.display = "block";
|
||||
@@ -348,6 +360,22 @@
|
||||
$("storage").appendChild(bar);
|
||||
}).catch(function (e) { st8.textContent = ""; toast("Status: " + e.message, 8000); });
|
||||
}
|
||||
function loadPublic() {
|
||||
$("public-host").textContent = publicHost || "ipcr-<domain>";
|
||||
return api("GET", "ipcr/config").then(function (c) {
|
||||
$("public-on").checked = !!c.public;
|
||||
$("public-state").textContent = c.public ? "on" : "off";
|
||||
$("public-try").textContent = "docker pull " + (publicHost || "ipcr-<domain>") + "/ipns/" + (pubName || publisher || "k51…") + "/<repo>:<tag>";
|
||||
});
|
||||
}
|
||||
$("public-on").onchange = function () {
|
||||
var cb = this, st = $("public-state");
|
||||
cb.disabled = true; st.textContent = ""; st.appendChild(spinner("saving…"));
|
||||
api("PATCH", "ipcr/config", { public: cb.checked })
|
||||
.then(function (c) { toast(c.public ? "Public pulls on (within 10 s)" : "Public pulls off (within 10 s)"); return loadPublic(); },
|
||||
function (e) { cb.checked = !cb.checked; toast(e.message, 8000); })
|
||||
.then(function () { cb.disabled = false; loadImages(); });
|
||||
};
|
||||
$("refresh").onclick = function () { busy(this, loadStatus(true)); };
|
||||
$("republish").onclick = function () {
|
||||
busy(this, api("POST", "ipcr/republish")).then(function () { toast("Published again"); loadStatus(true); });
|
||||
@@ -390,6 +418,7 @@
|
||||
var path = (short || img.repo) + ":" + tg.tag, rows = [["IPFS", reg + "/ipfs/" + tg.cid]];
|
||||
if (d.publisher) rows.push(["IPNS", reg + "/ipns/" + d.publisher + "/" + path]);
|
||||
if (d.name) rows.push([/\.eth$/.test(d.name) ? "ENS" : "DNS", reg + "/ipns/" + d.name + "/" + path]);
|
||||
if (d.public && publicHost && (d.name || d.publisher)) rows.push(["Public", publicHost + "/ipns/" + (d.name || d.publisher) + "/" + path]);
|
||||
if (short && (d.name || d.publisher)) rows.push(["Also", reg + "/ipns/" + (d.name || d.publisher) + "/" + img.repo + ":" + tg.tag]);
|
||||
var box = el("div");
|
||||
rows.forEach(function (r) {
|
||||
@@ -567,7 +596,7 @@
|
||||
}).catch(function (e) { toast(e.message, 8000); });
|
||||
};
|
||||
|
||||
loadStatus(false); loadImages(); loadRepos(); loadSettings();
|
||||
loadStatus(false).then(loadPublic); loadImages(); loadRepos(); loadSettings();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -206,6 +206,13 @@ image's content address and can never change.</p>
|
||||
<p class="muted">A repository that turns private or is deleted in Gitea is no longer synced
|
||||
(<i>frozen</i>). What was already published stays on the Radicle network: it cannot be taken back.</p>
|
||||
|
||||
<h2 id="anywhere">Pull without IPCR</h2>
|
||||
<p>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:</p>
|
||||
<pre><code id="anywhere-cmd">docker pull ipcr-<domain>/ipns/<name>/<repo>:1.0.0</code></pre>
|
||||
<p class="muted">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.</p>
|
||||
|
||||
<h2 id="install">Pull from another server</h2>
|
||||
<p>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.</p>
|
||||
@@ -231,6 +238,7 @@ for IPFS peers. Details: <a href="https://github.com/Yundera/ipcr" target="_blan
|
||||
<tr><td><a id="t-radicle" href="#"></a></td><td>Radicle explorer (public)</td></tr>
|
||||
<tr><td><a id="t-radicle-api" href="#"></a></td><td>Radicle API and read-only git over HTTPS (public)</td></tr>
|
||||
<tr><td><code>ipcr.localhost:4767</code></td><td>IPCR registry, for this server's Docker only</td></tr>
|
||||
<tr><td><a id="t-ipcr" href="#"></a></td><td>Public front door: <code>docker pull</code> this forge's images from anywhere (read-only)</td></tr>
|
||||
<tr><td>TCP 8776 · TCP/UDP 4768</td><td>Radicle peers · IPFS peers</td></tr>
|
||||
</table></div>
|
||||
|
||||
@@ -263,6 +271,8 @@ for IPFS peers. Details: <a href="https://github.com/Yundera/ipcr" target="_blan
|
||||
setLink("t-forge", "ipcr-forge", true);
|
||||
setLink("t-radicle", "radicle", true);
|
||||
setLink("t-radicle-api", "radicle-api", true);
|
||||
setLink("t-ipcr", "ipcr", true);
|
||||
if (base) document.getElementById("anywhere-cmd").textContent = "docker pull ipcr-" + base + "/ipns/<name>/<repo>:1.0.0";
|
||||
|
||||
function el(tag, text, cls) {
|
||||
var e = document.createElement(tag);
|
||||
@@ -335,6 +345,12 @@ for IPFS peers. Details: <a href="https://github.com/Yundera/ipcr" target="_blan
|
||||
pin.appendChild(document.createTextNode("this build only: "));
|
||||
pin.appendChild(el("code", reg + "/ipfs/" + info.cid));
|
||||
td2.appendChild(pin);
|
||||
if (pub.public && base) {
|
||||
var anywhere = el("div", null, "muted");
|
||||
anywhere.appendChild(document.createTextNode("without IPCR: "));
|
||||
anywhere.appendChild(el("code", "docker pull ipcr-" + base + "/ipns/" + path + ":" + tag));
|
||||
td2.appendChild(anywhere);
|
||||
}
|
||||
var td3 = el("td"); td3.appendChild(copyButton(line));
|
||||
tr.appendChild(td1); tr.appendChild(td2); tr.appendChild(td3);
|
||||
tb.t.appendChild(tr);
|
||||
|
||||
Reference in New Issue
Block a user