A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

attempt gorelease and goat builds

+1385 -11
+76
.goreleaser.yaml
··· 1 + # GoReleaser configuration for ATCR 2 + # See https://goreleaser.com for documentation 3 + 4 + version: 2 5 + 6 + before: 7 + hooks: 8 + - go mod tidy 9 + 10 + builds: 11 + # Credential helper - cross-platform native binary distribution 12 + - id: credential-helper 13 + binary: docker-credential-atcr 14 + main: ./cmd/credential-helper 15 + env: 16 + - CGO_ENABLED=0 17 + goos: 18 + - linux 19 + - darwin 20 + - windows 21 + goarch: 22 + - amd64 23 + - arm64 24 + ldflags: 25 + - -s -w 26 + - -X main.version={{.Version}} 27 + - -X main.commit={{.Commit}} 28 + - -X main.date={{.Date}} 29 + 30 + archives: 31 + - id: credential-helper 32 + format: tar.gz 33 + name_template: >- 34 + docker-credential-atcr_ 35 + {{- .Version }}_ 36 + {{- title .Os }}_ 37 + {{- if eq .Arch "amd64" }}x86_64 38 + {{- else if eq .Arch "386" }}i386 39 + {{- else }}{{ .Arch }}{{ end }} 40 + format_overrides: 41 + - goos: windows 42 + format: zip 43 + files: 44 + - LICENSE* 45 + - README* 46 + - INSTALLATION* 47 + 48 + checksum: 49 + name_template: 'checksums.txt' 50 + 51 + snapshot: 52 + version_template: "{{ incpatch .Version }}-next" 53 + 54 + changelog: 55 + sort: asc 56 + filters: 57 + exclude: 58 + - '^docs:' 59 + - '^test:' 60 + - '^chore:' 61 + - Merge pull request 62 + - Merge branch 63 + 64 + publishers: 65 + - name: atproto-pds 66 + cmd: ./scripts/publish-artifact.sh 67 + env: 68 + - APP_PASSWORD={{ .Env.APP_PASSWORD }} 69 + - REPO_URL={{ .Env.REPO_URL }} 70 + - TAG={{ .Tag }} 71 + - ARTIFACT_PATH={{ abs .ArtifactPath }} 72 + - ARTIFACT_NAME={{ .ArtifactName }} 73 + 74 + # Disable since not using GitHub 75 + release: 76 + disable: true
+33
.tangled/workflows/release.yml
··· 1 + # ATCR Release Pipeline for Tangled.org 2 + # Triggers on version tags and builds cross-platform binaries using GoReleaser 3 + 4 + when: 5 + - event: ["push"] 6 + # TODO: Trigger only on version tags (v1.0.0, v2.1.3, etc.) 7 + branch: ["main"] 8 + 9 + engine: "nixery" 10 + 11 + dependencies: 12 + nixpkgs: 13 + - git 14 + - go 15 + - goreleaser 16 + 17 + steps: 18 + - name: Fetch git tags 19 + command: git fetch --tags --force 20 + 21 + - name: Checkout latest tag 22 + command: git checkout $(git tag --sort=-version:refname | head -n1) 23 + 24 + - name: Tidy Go modules 25 + command: go mod tidy 26 + 27 + - name: Install Goat 28 + command: go install github.com/bluesky-social/goat@latest 29 + 30 + - name: Run GoReleaser 31 + command: goreleaser release --clean 32 + environment: 33 + REPO_URL
+260
INSTALLATION.md
··· 1 + # Installing ATCR Credential Helper 2 + 3 + The ATCR credential helper enables Docker to authenticate with ATCR registries using ATProto device authorization. 4 + 5 + ## Quick Install (Recommended) 6 + 7 + ### Using install script 8 + 9 + **Linux/macOS:** 10 + ```bash 11 + curl -fsSL https://atcr.io/install.sh | bash 12 + ``` 13 + 14 + Or download and run manually: 15 + 16 + ```bash 17 + curl -fsSLO https://atcr.io/install.sh 18 + chmod +x install.sh 19 + ./install.sh 20 + ``` 21 + 22 + Custom installation directory: 23 + 24 + ```bash 25 + INSTALL_DIR=$HOME/.local/bin curl -fsSL https://atcr.io/install.sh | bash 26 + ``` 27 + 28 + **Windows (PowerShell as Administrator):** 29 + ```powershell 30 + iwr -useb https://atcr.io/install.ps1 | iex 31 + ``` 32 + 33 + Or download and run manually: 34 + 35 + ```powershell 36 + Invoke-WebRequest -Uri https://atcr.io/install.ps1 -OutFile install.ps1 37 + .\install.ps1 38 + ``` 39 + 40 + ### Using Homebrew (macOS) 41 + You can read the full manifest spec here, but the dependencies block is the real interesting bit. Dependencies for your workflow, like Go, Node.js, Python etc. can be pulled in from nixpkgs. Nixpkgs—for the uninitiated—is a vast collection of packages for the Nix package manager. Fortunately, you needn’t know nor care about Nix to use it! Just head to https://search.nixos.org to find your package of choice (I’ll bet 1€ that it’s there1), toss it in the list and run your build. The Nix-savvy of you lot will be happy to know that you can use custom registries too. 42 + ```bash 43 + brew tap atcr-io/tap 44 + brew install docker-credential-atcr 45 + ``` 46 + 47 + ### Manual Installation 48 + 49 + 1. **Download the binary** for your platform from [GitHub Releases](https://github.com/atcr-io/atcr/releases) 50 + 51 + - Linux amd64: `docker-credential-atcr_VERSION_Linux_x86_64.tar.gz` 52 + - Linux arm64: `docker-credential-atcr_VERSION_Linux_arm64.tar.gz` 53 + - macOS amd64: `docker-credential-atcr_VERSION_Darwin_x86_64.tar.gz` 54 + - macOS arm64: `docker-credential-atcr_VERSION_Darwin_arm64.tar.gz` 55 + - Windows amd64: `docker-credential-atcr_VERSION_Windows_x86_64.zip` 56 + - Windows arm64: `docker-credential-atcr_VERSION_Windows_arm64.zip` 57 + 58 + 2. **Extract and install**: 59 + 60 + **Linux/macOS:** 61 + ```bash 62 + tar -xzf docker-credential-atcr_VERSION_OS_ARCH.tar.gz 63 + sudo install -m 755 docker-credential-atcr /usr/local/bin/ 64 + ``` 65 + 66 + **Windows (PowerShell as Administrator):** 67 + ```powershell 68 + Expand-Archive docker-credential-atcr_VERSION_Windows_x86_64.zip 69 + Move-Item docker-credential-atcr.exe C:\Windows\System32\ 70 + ``` 71 + 72 + 3. **Verify installation**: 73 + 74 + ```bash 75 + docker-credential-atcr version 76 + ``` 77 + 78 + ### From Source (requires Go 1.23+) 79 + 80 + ```bash 81 + go install atcr.io/cmd/credential-helper@latest 82 + sudo mv $(go env GOPATH)/bin/credential-helper /usr/local/bin/docker-credential-atcr 83 + ``` 84 + 85 + ## Configuration 86 + 87 + ### 1. Configure Docker 88 + 89 + Add the credential helper to Docker's config: 90 + 91 + ```bash 92 + # Create or edit ~/.docker/config.json 93 + cat > ~/.docker/config.json << 'EOF' 94 + { 95 + "credHelpers": { 96 + "atcr.io": "atcr" 97 + } 98 + } 99 + EOF 100 + ``` 101 + 102 + Or add to existing config: 103 + 104 + ```json 105 + { 106 + "credHelpers": { 107 + "atcr.io": "atcr", 108 + "docker.io": "desktop" 109 + } 110 + } 111 + ``` 112 + 113 + ### 2. Authenticate 114 + 115 + The credential helper will automatically trigger authentication when you first push/pull: 116 + 117 + ```bash 118 + docker push atcr.io/yourhandle/myapp:latest 119 + ``` 120 + 121 + This will: 122 + 1. Open your browser for device authorization 123 + 2. Display a code to confirm 124 + 3. Store credentials in `~/.atcr/device.json` 125 + 4. Exchange for registry JWT and proceed with push 126 + 127 + ### 3. Manual Authentication (optional) 128 + 129 + If you prefer to authenticate before pushing: 130 + 131 + ```bash 132 + # This triggers the device flow manually 133 + echo "atcr.io" | ATCR_AUTO_AUTH=1 docker-credential-atcr get > /dev/null 134 + ``` 135 + 136 + ## Usage 137 + 138 + Once configured, Docker commands work normally: 139 + 140 + ```bash 141 + # Push image 142 + docker push atcr.io/alice.bsky.social/myapp:latest 143 + 144 + # Pull image 145 + docker pull atcr.io/bob.bsky.social/coolapp:v1.2.3 146 + 147 + # Build and push 148 + docker build -t atcr.io/alice.bsky.social/web:latest . 149 + docker push atcr.io/alice.bsky.social/web:latest 150 + ``` 151 + 152 + ## Multiple Registries 153 + 154 + The credential helper supports multiple ATCR instances (e.g., production + self-hosted): 155 + 156 + ```json 157 + { 158 + "credHelpers": { 159 + "atcr.io": "atcr", 160 + "registry.mycompany.com": "atcr" 161 + } 162 + } 163 + ``` 164 + 165 + Credentials are stored per AppView URL in `~/.atcr/device.json`. 166 + 167 + ## Troubleshooting 168 + 169 + ### "credential helper not found" 170 + 171 + Ensure `docker-credential-atcr` is in your PATH: 172 + 173 + ```bash 174 + which docker-credential-atcr 175 + ``` 176 + 177 + If not found, add the installation directory to PATH: 178 + 179 + ```bash 180 + export PATH="/usr/local/bin:$PATH" 181 + ``` 182 + 183 + ### "No valid credentials found" 184 + 185 + Enable auto-auth and retry: 186 + 187 + ```bash 188 + docker push atcr.io/yourhandle/myapp:latest 189 + ``` 190 + 191 + ### "authorization failed" 192 + 193 + Check that you can access the AppView: 194 + 195 + ```bash 196 + curl -v https://atcr.io/v2/ 197 + ``` 198 + 199 + For local development (HTTP): 200 + 201 + ```json 202 + { 203 + "insecure-registries": ["localhost:5000"] 204 + } 205 + ``` 206 + 207 + Add to `/etc/docker/daemon.json` and restart Docker: 208 + 209 + ```bash 210 + sudo systemctl restart docker 211 + ``` 212 + 213 + ### Logout 214 + 215 + To remove stored credentials: 216 + 217 + ```bash 218 + echo "atcr.io" | docker-credential-atcr erase 219 + ``` 220 + 221 + Or delete the credentials file: 222 + 223 + ```bash 224 + rm ~/.atcr/device.json 225 + ``` 226 + 227 + ## Uninstall 228 + 229 + ```bash 230 + # Remove binary 231 + sudo rm /usr/local/bin/docker-credential-atcr 232 + 233 + # Remove credentials 234 + rm -rf ~/.atcr 235 + 236 + # Remove from Docker config 237 + # Edit ~/.docker/config.json and remove "atcr" from credHelpers 238 + ``` 239 + 240 + ## Platform Support 241 + 242 + | Platform | Arch | Status | 243 + |----------|------|--------| 244 + | Linux | amd64 | ✅ Supported | 245 + | Linux | arm64 | ✅ Supported | 246 + | macOS | amd64 (Intel) | ✅ Supported | 247 + | macOS | arm64 (Apple Silicon) | ✅ Supported | 248 + | Windows | amd64 | ✅ Supported | 249 + | Windows | arm64 | ✅ Supported | 250 + 251 + ## Security 252 + 253 + - Credentials are stored in `~/.atcr/device.json` with `0600` permissions (owner read/write only) 254 + - Device secrets are issued per-device and can be revoked via the AppView web UI 255 + - Authentication uses ATProto OAuth with device authorization flow 256 + - No passwords are stored locally 257 + 258 + ## Development 259 + 260 + See [CLAUDE.md](./CLAUDE.md#credential-helper-cmd-credential-helper) for development docs.
+23 -7
README.md
··· 216 216 - **middleware.repository**: ATProto routing middleware 217 217 - **middleware.registry**: Name resolution middleware 218 218 219 - ## Usage 219 + ## Installing Credential Helper 220 220 221 - ### Configure Credential Helper (Recommended) 221 + **Quick Install:** 222 222 223 223 ```bash 224 - # Build and configure the credential helper 225 - go build -o docker-credential-atcr ./cmd/credential-helper 226 - ./docker-credential-atcr configure 227 - # Follow the OAuth flow in your browser 224 + # Linux/macOS 225 + curl -fsSL https://atcr.io/install.sh | bash 226 + 227 + # Windows (PowerShell as Administrator) 228 + iwr -useb https://atcr.io/install.ps1 | iex 229 + ``` 228 230 229 - # Add to Docker config (~/.docker/config.json) 231 + For detailed installation instructions (Homebrew, manual install, etc.), see **[INSTALLATION.md](./INSTALLATION.md)**. 232 + 233 + **Configure Docker:** 234 + 235 + ```bash 236 + # Add to ~/.docker/config.json 230 237 { 231 238 "credHelpers": { 232 239 "atcr.io": "atcr" 233 240 } 234 241 } 242 + ``` 243 + 244 + ## Usage 245 + 246 + ### Authenticate 247 + 248 + ```bash 249 + # Auto-authentication on first push/pull 250 + docker push atcr.io/yourhandle/myapp:latest 235 251 ``` 236 252 237 253 ### Pushing an Image
+8
cmd/appview/serve.go
··· 506 506 }, 507 507 )).Methods("GET") 508 508 509 + // Install page (public) 510 + router.Handle("/install", middleware.OptionalAuth(sessionStore, database)( 511 + &uihandlers.InstallHandler{ 512 + Templates: templates, 513 + RegistryURL: uihandlers.TrimRegistryURL(baseURL), 514 + }, 515 + )).Methods("GET") 516 + 509 517 // API route for repository stats (public, read-only) 510 518 router.Handle("/api/stats/{handle}/{repository}", middleware.OptionalAuth(sessionStore, database)( 511 519 &uihandlers.GetStatsHandler{
+9 -1
cmd/credential-helper/main.go
··· 67 67 Error string `json:"error,omitempty"` 68 68 } 69 69 70 + var ( 71 + version = "dev" 72 + commit = "none" 73 + date = "unknown" 74 + ) 75 + 70 76 func main() { 71 77 if len(os.Args) < 2 { 72 - fmt.Fprintf(os.Stderr, "Usage: docker-credential-atcr <get|store|erase>\n") 78 + fmt.Fprintf(os.Stderr, "Usage: docker-credential-atcr <get|store|erase|version>\n") 73 79 os.Exit(1) 74 80 } 75 81 ··· 82 88 handleStore() 83 89 case "erase": 84 90 handleErase() 91 + case "version": 92 + fmt.Printf("docker-credential-atcr %s (commit: %s, built: %s)\n", version, commit, date) 85 93 default: 86 94 fmt.Fprintf(os.Stderr, "Unknown command: %s\n", command) 87 95 os.Exit(1)
+360
docs/DISTRIBUTION.md
··· 1 + # Distribution Strategy for ATCR Credential Helper 2 + 3 + ## Overview 4 + 5 + The ATCR credential helper is distributed as pre-built binaries for all major platforms using GoReleaser and GitHub Actions. This document outlines the complete distribution pipeline. 6 + 7 + ## Why Go is Ideal for Credential Helpers 8 + 9 + Go is actually the **perfect choice** for Docker credential helpers: 10 + 11 + 1. **Cross-compilation** - Single command builds for all platforms 12 + 2. **Static binaries** - No runtime dependencies (unlike Node.js, Python, Ruby) 13 + 3. **Industry standard** - Most Docker credential helpers are written in Go: 14 + - docker-credential-helpers (official) 15 + - docker-credential-gcr (Google) 16 + - docker-credential-ecr-login (AWS) 17 + - docker-credential-pass (community) 18 + 19 + ## Supported Platforms 20 + 21 + | Platform | Arch | Format | Status | 22 + |----------|------|--------|--------| 23 + | Linux | amd64 | tar.gz | ✅ | 24 + | Linux | arm64 | tar.gz | ✅ | 25 + | macOS | amd64 (Intel) | tar.gz | ✅ | 26 + | macOS | arm64 (Apple Silicon) | tar.gz | ✅ | 27 + | Windows | amd64 | zip | ✅ | 28 + | Windows | arm64 | zip | ✅ | 29 + 30 + ## Distribution Methods 31 + 32 + ### 1. GitHub Releases (Automated) 33 + 34 + **Trigger:** Push a git tag (e.g., `v1.0.0`) 35 + 36 + ```bash 37 + git tag v1.0.0 38 + git push origin v1.0.0 39 + ``` 40 + 41 + **What happens:** 42 + 1. GitHub Actions runs (`.github/workflows/release.yml`) 43 + 2. GoReleaser builds binaries for all platforms 44 + 3. Creates GitHub release with: 45 + - Pre-built binaries (tar.gz/zip) 46 + - Checksums file 47 + - Changelog 48 + 4. Updates Homebrew tap (if configured) 49 + 50 + **Workflow file:** `.github/workflows/release.yml` 51 + **Config file:** `.goreleaser.yaml` 52 + 53 + ### 2. Install Scripts 54 + 55 + **Linux/macOS:** `install.sh` 56 + - Detects OS and architecture 57 + - Downloads latest release from GitHub 58 + - Installs to `/usr/local/bin` (or custom dir) 59 + - Makes executable and verifies installation 60 + 61 + **Windows:** `install.ps1` 62 + - Detects architecture 63 + - Downloads latest release 64 + - Installs to `C:\Program Files\ATCR` (or custom dir) 65 + - Adds to system PATH 66 + - Requires Administrator privileges 67 + 68 + **Usage:** 69 + ```bash 70 + # Linux/macOS 71 + curl -fsSL https://atcr.io/install.sh | bash 72 + 73 + # Windows (PowerShell) 74 + iwr -useb https://atcr.io/install.ps1 | iex 75 + ``` 76 + 77 + ### 3. Homebrew (macOS) 78 + 79 + **Setup required:** 80 + 1. Create `atcr-io/homebrew-tap` repository 81 + 2. Set `HOMEBREW_TAP_TOKEN` secret in GitHub Actions 82 + 3. GoReleaser automatically updates formula on release 83 + 84 + **Usage:** 85 + ```bash 86 + brew tap atcr-io/tap 87 + brew install docker-credential-atcr 88 + ``` 89 + 90 + **Benefits:** 91 + - Automatic updates via `brew upgrade` 92 + - Handles PATH configuration 93 + - Native macOS experience 94 + 95 + ### 4. Manual Download 96 + 97 + Users can download directly from GitHub Releases: 98 + 99 + ```bash 100 + # Example: Linux amd64 101 + VERSION=v1.0.0 102 + curl -LO https://github.com/atcr-io/atcr/releases/download/${VERSION}/docker-credential-atcr_${VERSION#v}_Linux_x86_64.tar.gz 103 + tar -xzf docker-credential-atcr_${VERSION#v}_Linux_x86_64.tar.gz 104 + sudo install -m 755 docker-credential-atcr /usr/local/bin/ 105 + ``` 106 + 107 + ### 5. From Source 108 + 109 + For users with Go installed: 110 + 111 + ```bash 112 + go install atcr.io/cmd/credential-helper@latest 113 + sudo mv $(go env GOPATH)/bin/credential-helper /usr/local/bin/docker-credential-atcr 114 + ``` 115 + 116 + **Note:** This requires Go 1.23+ and compiles locally. 117 + 118 + ## Release Process 119 + 120 + ### Creating a New Release 121 + 122 + 1. **Update version** (if using version consts anywhere) 123 + 124 + 2. **Commit and tag:** 125 + ```bash 126 + git add . 127 + git commit -m "Release v1.0.0" 128 + git tag -a v1.0.0 -m "Release v1.0.0" 129 + git push origin main 130 + git push origin v1.0.0 131 + ``` 132 + 133 + 3. **Wait for CI:** 134 + - GitHub Actions builds and releases automatically 135 + - Check: https://github.com/atcr-io/atcr/actions 136 + 137 + 4. **Verify release:** 138 + - Visit: https://github.com/atcr-io/atcr/releases 139 + - Test install script: 140 + ```bash 141 + ATCR_VERSION=v1.0.0 curl -fsSL https://atcr.io/install.sh | bash 142 + docker-credential-atcr version 143 + ``` 144 + 145 + ### Version Information 146 + 147 + GoReleaser injects version info at build time: 148 + 149 + ```go 150 + var ( 151 + version = "dev" // Set to tag (e.g., "v1.0.0") 152 + commit = "none" // Set to git commit hash 153 + date = "unknown" // Set to build timestamp 154 + ) 155 + ``` 156 + 157 + Usage: 158 + ```bash 159 + $ docker-credential-atcr version 160 + docker-credential-atcr v1.0.0 (commit: abc123, built: 2025-01-15T10:30:00Z) 161 + ``` 162 + 163 + ## Distribution Configuration 164 + 165 + ### GoReleaser Config (`.goreleaser.yaml`) 166 + 167 + Key sections: 168 + 169 + **Builds:** 170 + - Binary name: `docker-credential-atcr` (Windows: `.exe` auto-added) 171 + - Targets: Linux, macOS, Windows (amd64, arm64) 172 + - CGO disabled for static binaries 173 + - Ldflags inject version info 174 + 175 + **Archives:** 176 + - Format: tar.gz (Linux/macOS), zip (Windows) 177 + - Naming: `docker-credential-atcr_VERSION_OS_ARCH.tar.gz` 178 + - Includes: LICENSE, README, INSTALLATION.md 179 + 180 + **Homebrew:** 181 + - Auto-updates tap repository 182 + - Formula includes version check 183 + - Installs to Homebrew prefix 184 + 185 + **Changelog:** 186 + - Auto-generated from commits 187 + - Filters out docs/test/chore commits 188 + 189 + ## Docker and Docker Desktop 190 + 191 + ### How Docker Finds Credential Helpers 192 + 193 + Docker looks for binaries named `docker-credential-*` in PATH: 194 + 195 + 1. User types: `docker push atcr.io/alice/app:latest` 196 + 2. Docker reads `~/.docker/config.json`: 197 + ```json 198 + { 199 + "credHelpers": { 200 + "atcr.io": "atcr" 201 + } 202 + } 203 + ``` 204 + 3. Docker looks for `docker-credential-atcr` in PATH 205 + 4. Calls: `docker-credential-atcr get` (with `atcr.io` on stdin) 206 + 5. Helper returns credentials (JSON on stdout) 207 + 208 + ### PATH Requirements 209 + 210 + **Linux/macOS:** 211 + - Common locations: `/usr/local/bin`, `/usr/bin`, `$HOME/.local/bin` 212 + - Check with: `which docker-credential-atcr` 213 + 214 + **Windows:** 215 + - Common locations: `C:\Windows\System32`, `C:\Program Files\ATCR` 216 + - Check with: `where docker-credential-atcr` 217 + 218 + ## CI/CD Secrets 219 + 220 + ### Required GitHub Secrets 221 + 222 + 1. **`GITHUB_TOKEN`** (automatic) 223 + - Provided by GitHub Actions 224 + - Used to create releases 225 + 226 + 2. **`HOMEBREW_TAP_TOKEN`** (manual setup) 227 + - Personal access token with `repo` scope 228 + - Used to update Homebrew tap 229 + - Can skip if not using Homebrew 230 + 231 + ### Setup Instructions 232 + 233 + ```bash 234 + # Create PAT with repo scope at: 235 + # https://github.com/settings/tokens 236 + 237 + # Add to repository secrets: 238 + # https://github.com/atcr-io/atcr/settings/secrets/actions 239 + ``` 240 + 241 + ## Testing the Distribution 242 + 243 + ### Before Tagging a Release 244 + 245 + 1. **Test GoReleaser locally:** 246 + ```bash 247 + goreleaser build --snapshot --clean 248 + ls dist/ 249 + ``` 250 + 251 + 2. **Test specific platform:** 252 + ```bash 253 + goreleaser build --snapshot --clean --single-target 254 + ./dist/docker-credential-atcr_*/docker-credential-atcr version 255 + ``` 256 + 257 + 3. **Test full release (dry run):** 258 + ```bash 259 + goreleaser release --snapshot --clean --skip=publish 260 + ``` 261 + 262 + ### After Release 263 + 264 + 1. **Test install script:** 265 + ```bash 266 + # Clean install in fresh environment 267 + docker run --rm -it ubuntu:latest bash 268 + apt update && apt install -y curl 269 + curl -fsSL https://atcr.io/install.sh | bash 270 + ``` 271 + 272 + 2. **Test Docker integration:** 273 + ```bash 274 + echo '{"credHelpers":{"atcr.io":"atcr"}}' > ~/.docker/config.json 275 + docker push atcr.io/test/image:latest 276 + ``` 277 + 278 + ## Future Enhancements 279 + 280 + ### Package Managers 281 + 282 + **Linux:** 283 + - `.deb` packages for Debian/Ubuntu (via GoReleaser) 284 + - `.rpm` packages for RHEL/Fedora/CentOS 285 + - AUR package for Arch Linux 286 + 287 + **macOS:** 288 + - Official Homebrew core (requires popularity/maturity) 289 + 290 + **Windows:** 291 + - Chocolatey package 292 + - Scoop manifest 293 + - Winget package 294 + 295 + ### Docker Distribution 296 + 297 + Could also distribute the credential helper via container: 298 + 299 + ```bash 300 + docker run --rm atcr.io/credential-helper:latest version 301 + 302 + # Install from container 303 + docker run --rm -v /usr/local/bin:/install \ 304 + atcr.io/credential-helper:latest \ 305 + cp /usr/local/bin/docker-credential-atcr /install/ 306 + ``` 307 + 308 + **Note:** Not recommended as primary method (users want native binaries), but useful for CI/CD pipelines. 309 + 310 + ## Troubleshooting 311 + 312 + ### "Binary not in PATH" 313 + 314 + **Symptom:** `docker push` fails with "credential helper not found" 315 + 316 + **Solution:** 317 + ```bash 318 + # Check if installed 319 + which docker-credential-atcr 320 + # or on Windows: 321 + where docker-credential-atcr 322 + 323 + # If not in PATH, add install dir to PATH 324 + export PATH="/usr/local/bin:$PATH" # Linux/macOS 325 + # or add to ~/.bashrc, ~/.zshrc 326 + ``` 327 + 328 + ### "Permission denied" 329 + 330 + **Symptom:** Can't execute binary 331 + 332 + **Solution:** 333 + ```bash 334 + chmod +x /usr/local/bin/docker-credential-atcr 335 + ``` 336 + 337 + ### "Wrong architecture" 338 + 339 + **Symptom:** `exec format error` or `bad CPU type` 340 + 341 + **Solution:** Download correct architecture: 342 + - x86_64/amd64 for Intel/AMD 343 + - arm64/aarch64 for Apple Silicon, ARM servers 344 + 345 + Check with: 346 + ```bash 347 + uname -m 348 + ``` 349 + 350 + ## Documentation 351 + 352 + - **User installation:** [INSTALLATION.md](../INSTALLATION.md) 353 + - **Project docs:** [CLAUDE.md](../CLAUDE.md) 354 + - **README:** [README.md](../README.md) 355 + 356 + ## References 357 + 358 + - [Docker Credential Helpers Spec](https://github.com/docker/docker-credential-helpers) 359 + - [GoReleaser Documentation](https://goreleaser.com) 360 + - [GitHub Actions: Publishing](https://docs.github.com/en/actions/publishing-packages)
+25
pkg/appview/handlers/install.go
··· 1 + package handlers 2 + 3 + import ( 4 + "html/template" 5 + "net/http" 6 + ) 7 + 8 + // InstallHandler handles the /install page 9 + type InstallHandler struct { 10 + Templates *template.Template 11 + RegistryURL string 12 + } 13 + 14 + func (h *InstallHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 15 + data := struct { 16 + PageData 17 + }{ 18 + PageData: NewPageData(r, h.RegistryURL), 19 + } 20 + 21 + if err := h.Templates.ExecuteTemplate(w, "install", data); err != nil { 22 + http.Error(w, err.Error(), http.StatusInternalServerError) 23 + return 24 + } 25 + }
+159
pkg/appview/static/install.ps1
··· 1 + # ATCR Credential Helper Installation Script for Windows 2 + # Usage: iwr -useb https://atcr.io/install.ps1 | iex 3 + 4 + $ErrorActionPreference = "Stop" 5 + 6 + # Configuration 7 + $Repo = "atcr-io/atcr" 8 + $BinaryName = "docker-credential-atcr.exe" 9 + $InstallDir = if ($env:ATCR_INSTALL_DIR) { $env:ATCR_INSTALL_DIR } else { "$env:ProgramFiles\ATCR" } 10 + 11 + Write-Host "ATCR Credential Helper Installer for Windows" -ForegroundColor Green 12 + Write-Host "" 13 + 14 + # Detect architecture 15 + function Get-Architecture { 16 + $arch = (Get-WmiObject Win32_Processor).Architecture 17 + switch ($arch) { 18 + 9 { return "x86_64" } # x64 19 + 12 { return "arm64" } # ARM64 20 + default { 21 + Write-Host "Unsupported architecture: $arch" -ForegroundColor Red 22 + exit 1 23 + } 24 + } 25 + } 26 + 27 + $Arch = Get-Architecture 28 + Write-Host "Detected: Windows $Arch" -ForegroundColor Green 29 + 30 + # Get latest release version 31 + function Get-LatestVersion { 32 + Write-Host "Fetching latest version..." -ForegroundColor Yellow 33 + $releaseUrl = "https://api.github.com/repos/$Repo/releases/latest" 34 + 35 + try { 36 + $release = Invoke-RestMethod -Uri $releaseUrl -UseBasicParsing 37 + return $release.tag_name 38 + } catch { 39 + Write-Host "Failed to fetch latest version: $_" -ForegroundColor Red 40 + exit 1 41 + } 42 + } 43 + 44 + $Version = if ($env:ATCR_VERSION) { $env:ATCR_VERSION } else { Get-LatestVersion } 45 + Write-Host "Latest version: $Version" -ForegroundColor Green 46 + 47 + # Download and install binary 48 + function Install-Binary { 49 + param ( 50 + [string]$Version, 51 + [string]$Arch 52 + ) 53 + 54 + $versionClean = $Version.TrimStart('v') 55 + $fileName = "docker-credential-atcr_${versionClean}_Windows_${Arch}.zip" 56 + $downloadUrl = "https://github.com/$Repo/releases/download/$Version/$fileName" 57 + 58 + Write-Host "Downloading from: $downloadUrl" -ForegroundColor Yellow 59 + 60 + $tempDir = New-Item -ItemType Directory -Path "$env:TEMP\atcr-install-$(Get-Random)" -Force 61 + $zipPath = Join-Path $tempDir $fileName 62 + 63 + try { 64 + Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -UseBasicParsing 65 + } catch { 66 + Write-Host "Failed to download release: $_" -ForegroundColor Red 67 + exit 1 68 + } 69 + 70 + Write-Host "Extracting..." -ForegroundColor Yellow 71 + Expand-Archive -Path $zipPath -DestinationPath $tempDir -Force 72 + 73 + # Create install directory 74 + if (-not (Test-Path $InstallDir)) { 75 + New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null 76 + } 77 + 78 + # Install binary 79 + $binaryPath = Join-Path $tempDir $BinaryName 80 + $destPath = Join-Path $InstallDir $BinaryName 81 + 82 + Copy-Item -Path $binaryPath -Destination $destPath -Force 83 + 84 + # Clean up 85 + Remove-Item -Path $tempDir -Recurse -Force 86 + 87 + Write-Host "Installed $BinaryName to $InstallDir" -ForegroundColor Green 88 + } 89 + 90 + # Add to PATH if not already present 91 + function Add-ToPath { 92 + $currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine") 93 + 94 + if ($currentPath -notlike "*$InstallDir*") { 95 + Write-Host "Adding $InstallDir to system PATH..." -ForegroundColor Yellow 96 + 97 + # Check if running as administrator 98 + $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) 99 + 100 + if ($isAdmin) { 101 + [Environment]::SetEnvironmentVariable("Path", "$currentPath;$InstallDir", "Machine") 102 + $env:Path = "$env:Path;$InstallDir" 103 + Write-Host "Added to PATH successfully" -ForegroundColor Green 104 + } else { 105 + Write-Host "WARNING: Not running as administrator. Cannot update system PATH." -ForegroundColor Yellow 106 + Write-Host "Please add $InstallDir to your PATH manually or re-run as administrator." -ForegroundColor Yellow 107 + } 108 + } 109 + } 110 + 111 + # Verify installation 112 + function Test-Installation { 113 + $binaryPath = Join-Path $InstallDir $BinaryName 114 + 115 + if (Test-Path $binaryPath) { 116 + Write-Host "Verification successful!" -ForegroundColor Green 117 + 118 + # Try to run version command 119 + try { 120 + & $binaryPath version 121 + } catch { 122 + Write-Host "Binary installed but PATH may need to be refreshed." -ForegroundColor Yellow 123 + Write-Host "Please restart your terminal or run: refreshenv" -ForegroundColor Yellow 124 + } 125 + } else { 126 + Write-Host "Installation failed: binary not found at $binaryPath" -ForegroundColor Red 127 + exit 1 128 + } 129 + } 130 + 131 + # Show configuration instructions 132 + function Show-Configuration { 133 + Write-Host "" 134 + Write-Host "Installation complete!" -ForegroundColor Green 135 + Write-Host "" 136 + Write-Host "To use ATCR with Docker, configure your credentials:" -ForegroundColor Yellow 137 + Write-Host " docker-credential-atcr configure" 138 + Write-Host "" 139 + Write-Host "Then configure Docker to use this credential helper:" -ForegroundColor Yellow 140 + Write-Host ' Edit %USERPROFILE%\.docker\config.json and add:' 141 + Write-Host ' { 142 + "credHelpers": { 143 + "atcr.io": "atcr" 144 + } 145 + }' 146 + Write-Host "" 147 + Write-Host "Note: You may need to restart your terminal for PATH changes to take effect." -ForegroundColor Yellow 148 + } 149 + 150 + # Main installation flow 151 + try { 152 + Install-Binary -Version $Version -Arch $Arch 153 + Add-ToPath 154 + Test-Installation 155 + Show-Configuration 156 + } catch { 157 + Write-Host "Installation failed: $_" -ForegroundColor Red 158 + exit 1 159 + }
+149
pkg/appview/static/install.sh
··· 1 + #!/bin/bash 2 + # ATCR Credential Helper Installation Script 3 + # Usage: curl -fsSL https://atcr.io/install.sh | bash 4 + 5 + set -e 6 + 7 + # Colors for output 8 + RED='\033[0;31m' 9 + GREEN='\033[0;32m' 10 + YELLOW='\033[1;33m' 11 + NC='\033[0m' # No Color 12 + 13 + # Configuration 14 + REPO="atcr-io/atcr" 15 + BINARY_NAME="docker-credential-atcr" 16 + INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}" 17 + 18 + # Detect OS and architecture 19 + detect_platform() { 20 + local os=$(uname -s | tr '[:upper:]' '[:lower:]') 21 + local arch=$(uname -m) 22 + 23 + case "$os" in 24 + linux*) 25 + OS="Linux" 26 + ;; 27 + darwin*) 28 + OS="Darwin" 29 + ;; 30 + *) 31 + echo -e "${RED}Unsupported OS: $os${NC}" 32 + exit 1 33 + ;; 34 + esac 35 + 36 + case "$arch" in 37 + x86_64|amd64) 38 + ARCH="x86_64" 39 + ;; 40 + aarch64|arm64) 41 + ARCH="arm64" 42 + ;; 43 + *) 44 + echo -e "${RED}Unsupported architecture: $arch${NC}" 45 + exit 1 46 + ;; 47 + esac 48 + } 49 + 50 + # Get latest release version 51 + get_latest_version() { 52 + echo -e "${YELLOW}Fetching latest version...${NC}" 53 + LATEST_VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') 54 + 55 + if [ -z "$LATEST_VERSION" ]; then 56 + echo -e "${RED}Failed to fetch latest version${NC}" 57 + exit 1 58 + fi 59 + 60 + echo -e "${GREEN}Latest version: ${LATEST_VERSION}${NC}" 61 + } 62 + 63 + # Download and install binary 64 + install_binary() { 65 + local version="${1:-$LATEST_VERSION}" 66 + local download_url="https://github.com/${REPO}/releases/download/${version}/docker-credential-atcr_${version#v}_${OS}_${ARCH}.tar.gz" 67 + 68 + echo -e "${YELLOW}Downloading from: ${download_url}${NC}" 69 + 70 + local tmp_dir=$(mktemp -d) 71 + trap "rm -rf $tmp_dir" EXIT 72 + 73 + if ! curl -fsSL "$download_url" -o "$tmp_dir/docker-credential-atcr.tar.gz"; then 74 + echo -e "${RED}Failed to download release${NC}" 75 + exit 1 76 + fi 77 + 78 + echo -e "${YELLOW}Extracting...${NC}" 79 + tar -xzf "$tmp_dir/docker-credential-atcr.tar.gz" -C "$tmp_dir" 80 + 81 + # Check if we need sudo 82 + if [ -w "$INSTALL_DIR" ]; then 83 + SUDO="" 84 + else 85 + SUDO="sudo" 86 + echo -e "${YELLOW}Installing to ${INSTALL_DIR} (requires sudo)${NC}" 87 + fi 88 + 89 + $SUDO mkdir -p "$INSTALL_DIR" 90 + $SUDO install -m 755 "$tmp_dir/$BINARY_NAME" "$INSTALL_DIR/$BINARY_NAME" 91 + 92 + echo -e "${GREEN}Installed ${BINARY_NAME} to ${INSTALL_DIR}${NC}" 93 + } 94 + 95 + # Verify installation 96 + verify_installation() { 97 + if ! command -v "$BINARY_NAME" &> /dev/null; then 98 + echo -e "${RED}${BINARY_NAME} not found in PATH${NC}" 99 + echo -e "${YELLOW}You may need to add ${INSTALL_DIR} to your PATH${NC}" 100 + echo -e "${YELLOW}Add this to your ~/.bashrc or ~/.zshrc:${NC}" 101 + echo -e " export PATH=\"${INSTALL_DIR}:\$PATH\"" 102 + return 1 103 + fi 104 + 105 + echo -e "${GREEN}Verification successful!${NC}" 106 + "$BINARY_NAME" version 107 + } 108 + 109 + # Configure Docker 110 + configure_docker() { 111 + echo "" 112 + echo -e "${GREEN}Installation complete!${NC}" 113 + echo "" 114 + echo -e "${YELLOW}To use ATCR with Docker, configure your credentials:${NC}" 115 + echo -e " ${BINARY_NAME} configure" 116 + echo "" 117 + echo -e "${YELLOW}Then configure Docker to use this credential helper:${NC}" 118 + echo -e ' echo '\''{"credHelpers": {"atcr.io": "atcr"}}'\'' > ~/.docker/config.json' 119 + echo "" 120 + echo -e "${YELLOW}Or add to existing config.json:${NC}" 121 + echo -e ' { 122 + "credHelpers": { 123 + "atcr.io": "atcr" 124 + } 125 + }' 126 + } 127 + 128 + # Main 129 + main() { 130 + echo -e "${GREEN}ATCR Credential Helper Installer${NC}" 131 + echo "" 132 + 133 + detect_platform 134 + echo -e "Detected: ${GREEN}${OS} ${ARCH}${NC}" 135 + 136 + # Allow specifying version via environment variable 137 + if [ -z "$ATCR_VERSION" ]; then 138 + get_latest_version 139 + else 140 + LATEST_VERSION="$ATCR_VERSION" 141 + echo -e "Using specified version: ${GREEN}${LATEST_VERSION}${NC}" 142 + fi 143 + 144 + install_binary 145 + verify_installation 146 + configure_docker 147 + } 148 + 149 + main "$@"
+223
pkg/appview/templates/pages/install.html
··· 1 + {{ define "install" }} 2 + <!DOCTYPE html> 3 + <html lang="en"> 4 + <head> 5 + <meta charset="UTF-8"> 6 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 + <title>Install ATCR Credential Helper - ATCR</title> 8 + <link rel="stylesheet" href="/static/css/style.css"> 9 + <script src="https://unpkg.com/htmx.org@1.9.10"></script> 10 + <style> 11 + .install-page { 12 + max-width: 800px; 13 + margin: 0 auto; 14 + padding: 2rem 1rem; 15 + } 16 + .install-section { 17 + margin: 2rem 0; 18 + } 19 + .install-section h2 { 20 + margin-bottom: 1rem; 21 + color: #1a1a1a; 22 + } 23 + .install-section h3 { 24 + margin: 1.5rem 0 0.5rem; 25 + color: #4a4a4a; 26 + font-size: 1.1rem; 27 + } 28 + .code-block { 29 + background: #f5f5f5; 30 + border: 1px solid #ddd; 31 + border-radius: 4px; 32 + padding: 1rem; 33 + margin: 0.5rem 0 1rem; 34 + overflow-x: auto; 35 + } 36 + .code-block code { 37 + font-family: 'Monaco', 'Menlo', monospace; 38 + font-size: 0.9rem; 39 + line-height: 1.5; 40 + } 41 + .platform-tabs { 42 + display: flex; 43 + gap: 0.5rem; 44 + border-bottom: 2px solid #e0e0e0; 45 + margin-bottom: 1rem; 46 + } 47 + .platform-tab { 48 + padding: 0.5rem 1rem; 49 + cursor: pointer; 50 + border: none; 51 + background: none; 52 + font-size: 1rem; 53 + color: #666; 54 + transition: all 0.2s; 55 + } 56 + .platform-tab:hover { 57 + color: #000; 58 + } 59 + .platform-tab.active { 60 + color: #0066cc; 61 + border-bottom: 2px solid #0066cc; 62 + margin-bottom: -2px; 63 + } 64 + .platform-content { 65 + display: none; 66 + } 67 + .platform-content.active { 68 + display: block; 69 + } 70 + .note { 71 + background: #fff3cd; 72 + border-left: 4px solid #ffc107; 73 + padding: 1rem; 74 + margin: 1rem 0; 75 + } 76 + .success { 77 + background: #d4edda; 78 + border-left: 4px solid #28a745; 79 + padding: 1rem; 80 + margin: 1rem 0; 81 + } 82 + </style> 83 + </head> 84 + <body> 85 + {{ template "nav" . }} 86 + 87 + <main class="container"> 88 + <div class="install-page"> 89 + <h1>Install ATCR Credential Helper</h1> 90 + <p>The ATCR credential helper enables Docker to authenticate with ATCR registries using your ATProto identity.</p> 91 + 92 + <div class="install-section"> 93 + <h2>Quick Install</h2> 94 + 95 + <div class="platform-tabs"> 96 + <button class="platform-tab active" onclick="showPlatform('linux')">Linux / macOS</button> 97 + <button class="platform-tab" onclick="showPlatform('windows')">Windows</button> 98 + </div> 99 + 100 + <div class="platform-content active" id="linux-content"> 101 + <h3>Using install script</h3> 102 + <div class="code-block"><code>curl -fsSL {{ .RegistryURL }}/static/install.sh | bash</code></div> 103 + 104 + <h3>Manual download</h3> 105 + <div class="code-block"><code>curl -fsSLO {{ .RegistryURL }}/static/install.sh 106 + chmod +x install.sh 107 + ./install.sh</code></div> 108 + </div> 109 + 110 + <div class="platform-content" id="windows-content"> 111 + <h3>Using PowerShell (Run as Administrator)</h3> 112 + <div class="code-block"><code>iwr -useb {{ .RegistryURL }}/static/install.ps1 | iex</code></div> 113 + 114 + <h3>Manual download</h3> 115 + <div class="code-block"><code>Invoke-WebRequest -Uri {{ .RegistryURL }}/static/install.ps1 -OutFile install.ps1 116 + .\install.ps1</code></div> 117 + </div> 118 + </div> 119 + 120 + <div class="install-section"> 121 + <h2>Configure Docker</h2> 122 + <p>After installation, configure Docker to use the credential helper:</p> 123 + 124 + <h3>Create or edit <code>~/.docker/config.json</code></h3> 125 + <div class="code-block"><code>{ 126 + "credHelpers": { 127 + "{{ .RegistryURL }}": "atcr" 128 + } 129 + }</code></div> 130 + 131 + <div class="note"> 132 + <strong>Note:</strong> If you have an existing <code>config.json</code>, add the <code>credHelpers</code> entry to your existing configuration. 133 + </div> 134 + </div> 135 + 136 + <div class="install-section"> 137 + <h2>Authentication</h2> 138 + <p>The credential helper will automatically prompt for authentication when you push or pull:</p> 139 + 140 + <div class="code-block"><code>export ATCR_AUTO_AUTH=1 141 + docker push {{ .RegistryURL }}/yourhandle/myapp:latest</code></div> 142 + 143 + <p>This will:</p> 144 + <ol> 145 + <li>Open your browser for device authorization</li> 146 + <li>Display a code to confirm</li> 147 + <li>Store credentials securely</li> 148 + <li>Proceed with your push/pull operation</li> 149 + </ol> 150 + 151 + <div class="success"> 152 + <strong>Success!</strong> Once authenticated, Docker commands work normally without any additional steps. 153 + </div> 154 + </div> 155 + 156 + <div class="install-section"> 157 + <h2>Alternative: Use docker login</h2> 158 + <p>You can also use <code>docker login</code> with your ATProto app password:</p> 159 + 160 + <ol> 161 + <li>Generate an app password in your ATProto account settings</li> 162 + <li>Run: <code>docker login {{ .RegistryURL }}</code></li> 163 + <li>Enter your handle as username</li> 164 + <li>Enter your app password</li> 165 + </ol> 166 + 167 + <div class="note"> 168 + <strong>Note:</strong> App passwords are available in your Bluesky account settings under "App Passwords". 169 + </div> 170 + </div> 171 + 172 + <div class="install-section"> 173 + <h2>Troubleshooting</h2> 174 + 175 + <h3>Command not found</h3> 176 + <p>Ensure the credential helper is in your PATH:</p> 177 + <div class="code-block"><code># Check if installed 178 + which docker-credential-atcr 179 + 180 + # Add to PATH if needed 181 + export PATH="/usr/local/bin:$PATH"</code></div> 182 + 183 + <h3>Authentication failed</h3> 184 + <p>Make sure auto-auth is enabled:</p> 185 + <div class="code-block"><code>export ATCR_AUTO_AUTH=1</code></div> 186 + 187 + <h3>Still having issues?</h3> 188 + <p>Check the <a href="https://github.com/atcr-io/atcr/blob/main/INSTALLATION.md">full documentation</a> or <a href="https://github.com/atcr-io/atcr/issues">open an issue</a>.</p> 189 + </div> 190 + 191 + <div class="install-section"> 192 + <h2>Security</h2> 193 + <ul> 194 + <li>Credentials are stored in <code>~/.atcr/device.json</code> with secure permissions (0600)</li> 195 + <li>Device secrets are issued per-device and can be revoked anytime</li> 196 + <li>No passwords are stored locally</li> 197 + <li>Uses ATProto OAuth with device authorization flow</li> 198 + </ul> 199 + </div> 200 + </div> 201 + </main> 202 + 203 + <script> 204 + function showPlatform(platform) { 205 + // Update tabs 206 + document.querySelectorAll('.platform-tab').forEach(tab => { 207 + tab.classList.remove('active'); 208 + }); 209 + event.target.classList.add('active'); 210 + 211 + // Update content 212 + document.querySelectorAll('.platform-content').forEach(content => { 213 + content.classList.remove('active'); 214 + }); 215 + document.getElementById(platform + '-content').classList.add('active'); 216 + } 217 + </script> 218 + 219 + <div id="modal"></div> 220 + <script src="/static/js/app.js"></script> 221 + </body> 222 + </html> 223 + {{ end }}
+42 -3
pkg/auth/token/handler.go
··· 56 56 username, password, ok := r.BasicAuth() 57 57 if !ok { 58 58 fmt.Printf("DEBUG [token/handler]: No Basic auth credentials provided\n") 59 - w.Header().Set("WWW-Authenticate", `Basic realm="ATCR Registry"`) 59 + // Get base URL for install instructions 60 + baseURL := r.Header.Get("X-Forwarded-Host") 61 + if baseURL == "" { 62 + baseURL = r.Host 63 + } 64 + if !strings.HasPrefix(baseURL, "http") { 65 + // Add scheme 66 + if r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https" { 67 + baseURL = "https://" + baseURL 68 + } else { 69 + baseURL = "http://" + baseURL 70 + } 71 + } 72 + w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm="ATCR Registry - Visit %s/install to authenticate or use 'docker login' with your ATProto app-password"`, baseURL)) 60 73 http.Error(w, "authentication required", http.StatusUnauthorized) 61 74 return 62 75 } ··· 88 101 device, err := h.deviceStore.ValidateDeviceSecret(password) 89 102 if err != nil { 90 103 fmt.Printf("DEBUG [token/handler]: Device secret validation failed: %v\n", err) 91 - w.Header().Set("WWW-Authenticate", `Basic realm="ATCR Registry"`) 104 + // Get base URL for install instructions 105 + baseURL := r.Header.Get("X-Forwarded-Host") 106 + if baseURL == "" { 107 + baseURL = r.Host 108 + } 109 + if !strings.HasPrefix(baseURL, "http") { 110 + // Add scheme 111 + if r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https" { 112 + baseURL = "https://" + baseURL 113 + } else { 114 + baseURL = "http://" + baseURL 115 + } 116 + } 117 + w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm="ATCR Registry - Visit %s/install to authenticate or use 'docker login' with your ATProto app-password"`, baseURL)) 92 118 http.Error(w, "authentication failed", http.StatusUnauthorized) 93 119 return 94 120 } ··· 103 129 did, handle, accessToken, err = h.validator.CreateSessionAndGetToken(r.Context(), username, password) 104 130 if err != nil { 105 131 fmt.Printf("DEBUG [token/handler]: App password validation failed: %v\n", err) 106 - w.Header().Set("WWW-Authenticate", `Basic realm="ATCR Registry"`) 132 + // Get base URL for install instructions 133 + baseURL := r.Header.Get("X-Forwarded-Host") 134 + if baseURL == "" { 135 + baseURL = r.Host 136 + } 137 + if !strings.HasPrefix(baseURL, "http") { 138 + // Add scheme 139 + if r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https" { 140 + baseURL = "https://" + baseURL 141 + } else { 142 + baseURL = "http://" + baseURL 143 + } 144 + } 145 + w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm="ATCR Registry - Visit %s/install to authenticate or use 'docker login' with your ATProto app-password"`, baseURL)) 107 146 http.Error(w, fmt.Sprintf("authentication failed: %v", err), http.StatusUnauthorized) 108 147 return 109 148 }
+18
scripts/publish-artifact.sh
··· 1 + #!/usr/bin/env bash 2 + set -e 3 + 4 + TAG_HASH=$(git rev-parse "$TAG"^{tag}) && 5 + TAG_BYTES=$(echo -n "$TAG_HASH" | xxd -r -p | base64 | tr -d '=') && 6 + BLOB_OUTPUT=$(goat blob upload "$ARTIFACT_PATH") && 7 + echo "$BLOB_OUTPUT" && 8 + ARTIFACT_JSON=$(echo "$BLOB_OUTPUT" | jq --arg tag "$TAG_BYTES" --arg name "$ARTIFACT_NAME" --arg repo "$REPO_URL" --arg created "$(date -Iseconds)" '{ 9 + "tag": {"$bytes": $tag}, 10 + "name": $name, 11 + "repo": $repo, 12 + "$type": "sh.tangled.repo.artifact", 13 + "artifact": ., 14 + "createdAt": $created 15 + }') && 16 + echo "$ARTIFACT_JSON" > temp_artifact.json && 17 + cat temp_artifact.json && 18 + goat record create temp_artifact.json -n