mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-24 03:04:16 +00:00
seamark.dev's /install and /settings/devices told users to pipe
seamark.dev/static/install.sh into bash. That file was the unmodified ATCR
script: it announced itself as the "ATCR Credential Helper Installer",
installed docker-credential-atcr, and finished by telling the user to configure
credHelpers for atcr.io, the wrong registry for that deployment. Anyone
following the documented setup ended up pointed at another service. The
templates hardcoded docker-credential-atcr, "atcr" and ~/.atcr/device.json
alongside a correctly themed {{ .RegistryURL }}.
The scripts are now rendered from config by a handler, rather than forked per
brand. A theme overlay was the alternative and was worse: it needed a full copy
of both install.sh and install.ps1 per brand, four scripts to keep in sync, and
the operator asked for these values to come from config.
credential_helper.name is the single knob. Docker resolves a credHelpers value
x by exec'ing docker-credential-x, so the credHelpers value, the binary suffix
and the config directory are genuinely one word, not three that can drift. It
is validated against a strict pattern because it is interpolated into a shell
script.
install.sh renders byte-identical to the deleted static file under the atcr
default, so existing installs are unaffected. install.ps1 differs by one line,
where a stale usage comment named a path the script is not served at.
Two behaviour changes worth noting: these two URLs drop from a one-year
Cache-Control to five minutes, since the body now depends on deployment config;
and credential_helper.tangled_repo becomes a real overridable default. It was
previously assigned over unconditionally and read by nothing, while the shipped
script used a different URL form.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PDqoCE1j3njokkZ9b1C5n9
148 lines
8.3 KiB
HTML
148 lines
8.3 KiB
HTML
{{ define "install" }}
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
{{ template "head" . }}
|
|
{{ template "meta" .Meta }}
|
|
</head>
|
|
<body>
|
|
{{ template "nav" . }}
|
|
|
|
<main id="main-content" class="container mx-auto px-4 py-8 max-w-4xl">
|
|
<h1 class="text-3xl font-display font-bold tracking-tight mb-2">Install {{ .ClientShortName }} Credential Helper</h1>
|
|
<p class="text-base-content/70 mb-8">The {{ .ClientShortName }} credential helper enables Docker to authenticate with {{ .ClientShortName }} registries using your ATProto identity.</p>
|
|
|
|
<div class="space-y-12">
|
|
<section>
|
|
<h2 class="text-xl font-semibold mb-4">Quick Install</h2>
|
|
|
|
<div class="flex gap-2 mb-4" role="tablist" aria-label="Platform">
|
|
<button type="button" role="tab" id="linux-tab"
|
|
aria-selected="true" aria-controls="linux-content"
|
|
class="btn btn-sm btn-primary platform-tab" data-platform="linux">Linux / macOS</button>
|
|
<button type="button" role="tab" id="windows-tab"
|
|
aria-selected="false" aria-controls="windows-content" tabindex="-1"
|
|
class="btn btn-sm btn-ghost platform-tab" data-platform="windows">Windows</button>
|
|
</div>
|
|
|
|
<div class="platform-content" id="linux-content" role="tabpanel" aria-labelledby="linux-tab">
|
|
<h3 class="text-lg font-medium mb-3">Using install script</h3>
|
|
<div class="mockup-code bg-base-300 text-base-content mb-6">
|
|
<pre data-prefix="$"><code>curl -fsSL {{ .SiteURL }}/static/install.sh | bash</code></pre>
|
|
</div>
|
|
|
|
<h3 class="text-lg font-medium mb-3">Manual download</h3>
|
|
<div class="mockup-code bg-base-300 text-base-content">
|
|
<pre data-prefix="$"><code>curl -fsSLO {{ .SiteURL }}/static/install.sh</code></pre>
|
|
<pre data-prefix="$"><code>chmod +x install.sh</code></pre>
|
|
<pre data-prefix="$"><code>./install.sh</code></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="platform-content hidden" id="windows-content" role="tabpanel" aria-labelledby="windows-tab" hidden>
|
|
<h3 class="text-lg font-medium mb-3">Using PowerShell (Run as Administrator)</h3>
|
|
<div class="mockup-code bg-base-300 text-base-content mb-6">
|
|
<pre data-prefix="PS"><code>iwr -useb {{ .SiteURL }}/static/install.ps1 | iex</code></pre>
|
|
</div>
|
|
|
|
<h3 class="text-lg font-medium mb-3">Manual download</h3>
|
|
<div class="mockup-code bg-base-300 text-base-content">
|
|
<pre data-prefix="PS"><code>Invoke-WebRequest -Uri {{ .SiteURL }}/static/install.ps1 -OutFile install.ps1</code></pre>
|
|
<pre data-prefix="PS"><code>.\install.ps1</code></pre>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2 class="text-xl font-semibold mb-4">Configure Docker</h2>
|
|
<p class="mb-4">After installation, configure Docker to use the credential helper:</p>
|
|
|
|
<h3 class="text-lg font-medium mb-3">Create or edit <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">~/.docker/config.json</code></h3>
|
|
<div class="mockup-code bg-base-300 text-base-content mb-4">
|
|
<pre><code>{</code></pre>
|
|
<pre><code> "credHelpers": {</code></pre>
|
|
<pre><code> "{{ .RegistryURL }}": "{{ .CredHelper.Name }}"</code></pre>
|
|
<pre><code> }</code></pre>
|
|
<pre><code>}</code></pre>
|
|
</div>
|
|
|
|
<div class="alert alert-info">
|
|
{{ icon "info" "size-5" }}
|
|
<span>If you have an existing <code class="bg-base-300 px-1 py-0.5 rounded text-sm font-mono">config.json</code>, add the <code class="bg-base-300 px-1 py-0.5 rounded text-sm font-mono">credHelpers</code> entry to your existing configuration.</span>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2 class="text-xl font-semibold mb-4">Authentication</h2>
|
|
<p class="mb-4">The credential helper will automatically prompt for authentication when you push or pull:</p>
|
|
|
|
<div class="mockup-code bg-base-300 text-base-content mb-4">
|
|
<pre data-prefix="$"><code>docker push {{ .RegistryURL }}/yourhandle/myapp:latest</code></pre>
|
|
</div>
|
|
|
|
<p class="mb-4">This will:</p>
|
|
<ol class="list-decimal list-inside space-y-2 ml-4 mb-6">
|
|
<li>Open your browser for device authorization</li>
|
|
<li>Display a code to confirm</li>
|
|
<li>Store credentials securely</li>
|
|
<li>Proceed with your push/pull operation</li>
|
|
</ol>
|
|
|
|
<div class="alert alert-success">
|
|
{{ icon "check-circle" "size-5" }}
|
|
<span>Once authenticated, Docker commands work normally without any additional steps.</span>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2 class="text-xl font-semibold mb-4">Alternative: Use docker login</h2>
|
|
<p class="mb-4">You can also use <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">docker login</code> with your ATProto app password:</p>
|
|
|
|
<ol class="list-decimal list-inside space-y-2 ml-4 mb-6">
|
|
<li>Generate an app password at <a href="https://bsky.app/settings/app-passwords" target="_blank" rel="noopener noreferrer" class="link link-primary">bsky.app/settings/app-passwords</a></li>
|
|
<li>Run: <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">docker login {{ .RegistryURL }}</code></li>
|
|
<li>Enter your handle as username</li>
|
|
<li>Enter your app password</li>
|
|
</ol>
|
|
|
|
<div class="alert alert-info">
|
|
{{ icon "info" "size-5" }}
|
|
<span>Create an app password at <a href="https://bsky.app/settings/app-passwords" target="_blank" rel="noopener noreferrer" class="underline font-medium hover:no-underline">bsky.app/settings/app-passwords</a>.</span>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2 class="text-xl font-semibold mb-4">Troubleshooting</h2>
|
|
|
|
<h3 class="text-lg font-medium mb-3">Command not found</h3>
|
|
<p class="mb-4">Ensure the credential helper is in your PATH:</p>
|
|
<div class="mockup-code bg-base-300 text-base-content mb-6">
|
|
<pre data-prefix="#" class="text-base-content/70"><code>Check if installed</code></pre>
|
|
<pre data-prefix="$"><code>which {{ .CredHelper.BinaryName }}</code></pre>
|
|
<pre></pre>
|
|
<pre data-prefix="#" class="text-base-content/70"><code>Add to PATH if needed</code></pre>
|
|
<pre data-prefix="$"><code>export PATH="/usr/local/bin:$PATH"</code></pre>
|
|
</div>
|
|
|
|
<h3 class="text-lg font-medium mb-3">Still having issues?</h3>
|
|
<p>Check the <a href="https://tangled.org/evan.jarrett.net/at-container-registry/blob/main/INSTALLATION.md" class="link link-primary">full documentation</a> or <a href="https://tangled.org/evan.jarrett.net/at-container-registry/issues" class="link link-primary">open an issue</a>.</p>
|
|
</section>
|
|
|
|
<section>
|
|
<h2 class="text-xl font-semibold mb-4">Security</h2>
|
|
<ul class="list-disc list-inside space-y-2 ml-4">
|
|
<li>Credentials are stored in <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">{{ .CredHelper.DeviceFile }}</code> with secure permissions (0600)</li>
|
|
<li>Device secrets are issued per-device and can be revoked anytime</li>
|
|
<li>No passwords are stored locally</li>
|
|
<li>Uses ATProto OAuth with device authorization flow</li>
|
|
</ul>
|
|
</section>
|
|
</div>
|
|
</main>
|
|
|
|
|
|
{{ template "footer" . }}
|
|
</body>
|
|
</html>
|
|
{{ end }}
|