···11+// Package appview implements the ATCR AppView component, which serves as the main
22+// OCI Distribution API server. It resolves identities (handle/DID to PDS endpoint),
33+// routes manifests to user's PDS, routes blobs to hold services, validates OAuth tokens,
44+// and issues registry JWTs. This package provides environment-based configuration,
55+// middleware registration, and HTTP server setup for the AppView service.
16package appview
2738import (
+4
pkg/appview/db/schema.go
···11+// Package db provides the database layer for the AppView web UI, including
22+// SQLite schema initialization, migrations, and query functions for OAuth
33+// sessions, device flows, repository metadata, stars, pull counts, and
44+// user profiles.
15package db
2637import (
+3
pkg/appview/handlers/home.go
···11+// Package handlers provides HTTP handlers for the AppView web UI, including
22+// home page, repository browsing, search, user authentication, settings,
33+// device management, and API endpoints for the web interface.
14package handlers
2536import (
+3
pkg/appview/holdhealth/checker.go
···11+// Package holdhealth provides health checking for hold service endpoints.
22+// It periodically checks hold availability and caches health status with
33+// configurable TTL to avoid excessive health check requests.
14package holdhealth
2536import (
+3
pkg/appview/holdhealth/checker_test.go
···131131 status := checker.GetStatus(context.Background(), endpoint)
132132 if status == nil {
133133 t.Fatal("GetStatus returned nil")
134134+ return
134135 }
135136136137 if !status.Reachable {
···155156 status := checker.GetStatus(context.Background(), server.URL)
156157 if status == nil {
157158 t.Fatal("GetStatus returned nil")
159159+ return
158160 }
159161160162 if !status.Reachable {
···191193 status := checker.GetCachedStatus(endpoint)
192194 if status == nil {
193195 t.Fatal("Status not found in cache")
196196+ return
194197 }
195198196199 if !status.Reachable {
+3
pkg/appview/jetstream/worker.go
···11+// Package jetstream provides an ATProto Jetstream consumer for real-time updates.
22+// It connects to the Bluesky Jetstream WebSocket, processes repository events,
33+// indexes manifests and tags, and populates the AppView database for the web UI.
14package jetstream
2536import (
+4
pkg/appview/licenses/licenses.go
···11+// Package licenses provides SPDX license validation and parsing for container
22+// image annotations. It embeds the official SPDX license list and provides
33+// functions to look up license identifiers, validate them, and parse
44+// multi-license strings with fuzzy matching support.
15package licenses
2637//go:generate curl -fsSL -o spdx-licenses.json https://spdx.org/licenses/licenses.json
+4
pkg/appview/middleware/auth.go
···11+// Package middleware provides HTTP middleware for AppView, including
22+// authentication (session-based for web UI, token-based for registry),
33+// identity resolution (handle/DID to PDS endpoint), and hold discovery
44+// for routing blobs to storage endpoints.
15package middleware
2637import (
+4
pkg/appview/readme/cache.go
···11+// Package readme provides README fetching, rendering, and caching functionality
22+// for container repositories. It fetches markdown content from URLs, renders it
33+// to sanitized HTML using GitHub-flavored markdown, and caches the results in
44+// a database with configurable TTL.
15package readme
2637import (
···11+// Package storage implements the storage routing layer for AppView.
22+// It routes manifests to ATProto PDS (as io.atcr.manifest records) and
33+// blobs to hold services via XRPC, with hold DID caching for efficient pulls.
44+// All storage operations are proxied - AppView stores nothing locally.
15package storage
2637import (
+3-3
pkg/atproto/endpoints.go
···11-// Package xrpc provides constants for XRPC endpoint paths used throughout ATCR.
11+// Package atproto provides constants for XRPC endpoint paths used throughout ATCR.
22//
33// This package serves as a single source of truth for all XRPC endpoint URLs,
44// preventing typos and making refactoring easier. All endpoint paths follow the
···1515 // Response: {"uploadId": "..."}
1616 HoldInitiateUpload = "/xrpc/io.atcr.hold.initiateUpload"
17171818- // HoldGetPartUploadUrl gets a presigned URL or endpoint info for uploading a specific part.
1818+ // HoldGetPartUploadURL gets a presigned URL or endpoint info for uploading a specific part.
1919 // Method: POST
2020 // Request: {"uploadId": "...", "partNumber": 1}
2121 // Response: {"url": "...", "method": "PUT", "headers": {...}}
2222- HoldGetPartUploadUrl = "/xrpc/io.atcr.hold.getPartUploadUrl"
2222+ HoldGetPartUploadURL = "/xrpc/io.atcr.hold.getPartUploadUrl"
23232424 // HoldUploadPart handles direct buffered part uploads (alternative to presigned URLs).
2525 // Method: PUT
+1-1
pkg/atproto/profile.go
···99 "time"
1010)
11111212-// Profile record key is always "self" per lexicon
1212+// ProfileRKey is always "self" per lexicon
1313const ProfileRKey = "self"
14141515// Global map to track in-flight profile migrations (DID -> true)
+4
pkg/auth/oauth/client.go
···11+// Package oauth provides OAuth client and flow implementation for ATCR.
22+// It wraps indigo's OAuth library with ATCR-specific configuration,
33+// including default scopes, client metadata, token refreshing, and
44+// interactive browser-based authentication flows.
15package oauth
2637import (
+5-1
pkg/auth/session.go
···11+// Package auth provides authentication and authorization for ATCR, including
22+// ATProto session validation, hold authorization (captain/crew membership),
33+// scope parsing, and token caching for OAuth and service tokens.
14package auth
2536import (
44- "atcr.io/pkg/atproto"
57 "bytes"
68 "context"
79 "crypto/sha256"
···1214 "net/http"
1315 "sync"
1416 "time"
1717+1818+ "atcr.io/pkg/atproto"
15191620 "github.com/bluesky-social/indigo/atproto/identity"
1721 "github.com/bluesky-social/indigo/atproto/syntax"
+4
pkg/auth/token/cache.go
···11+// Package token provides service token caching and management for AppView.
22+// Service tokens are JWTs issued by a user's PDS to authorize AppView to
33+// act on their behalf when communicating with hold services. Tokens are
44+// cached with automatic expiry parsing and 10-second safety margins.
15package token
2637import (
+5
pkg/hold/config.go
···11+// Package hold implements the ATCR hold service, which provides BYOS
22+// (Bring Your Own Storage) functionality. It includes an embedded PDS for
33+// storing captain and crew records, generates presigned URLs for blob storage,
44+// and handles authorization based on crew membership. Configuration is loaded
55+// entirely from environment variables.
16package hold
2738import (
+3
pkg/hold/oci/http_helpers.go
···11+// Package oci provides HTTP helpers for OCI registry endpoints in the hold service.
22+// It includes utilities for JSON encoding/decoding of request/response bodies
33+// and standardized error responses for XRPC endpoints.
14package oci
2536import (
+1-1
pkg/hold/oci/multipart.go
···2626 Buffered
2727)
28282929-// CompletedPart represents an uploaded part with its ETag
2929+// PartInfo represents an uploaded part with its ETag
3030type PartInfo struct {
3131 PartNumber int `json:"part_number"`
3232 ETag string `json:"etag"`
+3-3
pkg/hold/oci/xrpc.go
···4444 r.Use(h.requireBlobWriteAccess)
45454646 r.Post(atproto.HoldInitiateUpload, h.HandleInitiateUpload)
4747- r.Post(atproto.HoldGetPartUploadUrl, h.HandleGetPartUploadUrl)
4747+ r.Post(atproto.HoldGetPartUploadURL, h.HandleGetPartUploadURL)
4848 r.Put(atproto.HoldUploadPart, h.HandleUploadPart)
4949 r.Post(atproto.HoldCompleteUpload, h.HandleCompleteUpload)
5050 r.Post(atproto.HoldAbortUpload, h.HandleAbortUpload)
···8080 })
8181}
82828383-// HandleGetPartUploadUrl returns a presigned URL or endpoint info for uploading a part
8383+// HandleGetPartUploadURL returns a presigned URL or endpoint info for uploading a part
8484// Replaces the old "action: part" pattern
8585-func (h *XRPCHandler) HandleGetPartUploadUrl(w http.ResponseWriter, r *http.Request) {
8585+func (h *XRPCHandler) HandleGetPartUploadURL(w http.ResponseWriter, r *http.Request) {
8686 var req struct {
8787 UploadID string `json:"uploadId"`
8888 PartNumber int `json:"partNumber"`
···12501250 return rm.cs.WipeUserData(ctx, uid)
12511251}
1252125212531253-// technically identical to TakeDownRepo, for now
12531253+// ResetRepo is technically identical to TakeDownRepo, for now
12541254func (rm *RepoManager) ResetRepo(ctx context.Context, uid models.Uid) error {
12551255 unlock := rm.lockUser(ctx, uid)
12561256 defer unlock()
+1-1
pkg/hold/pds/xrpc.go
···12231223 json.NewEncoder(w).Encode(response)
12241224}
1225122512261226-// getPresignedURL generates a presigned URL for GET, HEAD, or PUT operations
12261226+// GetPresignedURL generates a presigned URL for GET, HEAD, or PUT operations
12271227// Distinguishes between ATProto blobs (per-DID) and OCI blobs (content-addressed)
12281228func (h *XRPCHandler) GetPresignedURL(ctx context.Context, operation string, digest string, did string) (string, error) {
12291229 var path string
+8-4
pkg/s3/types.go
···11+// Package s3 provides S3 client initialization and presigned URL generation
22+// for hold services. It supports S3, Storj, and Minio storage backends,
33+// with fallback to buffered proxy mode when presigned URLs are unavailable.
14package s3
2536import (
47 "fmt"
88+ "log"
99+ "strings"
1010+511 "github.com/aws/aws-sdk-go/aws"
612 "github.com/aws/aws-sdk-go/aws/credentials"
713 "github.com/aws/aws-sdk-go/aws/session"
814 "github.com/aws/aws-sdk-go/service/s3"
99- "log"
1010- "strings"
1115)
12161317type S3Service struct {
···1620 PathPrefix string // S3 path prefix (if any)
1721}
18221919-// initializes the S3 client for presigned URL generation
2323+// NewS3Service initializes the S3 client for presigned URL generation
2024// Returns nil error if S3 client is successfully initialized
2125// Returns error if storage is not S3 or if initialization fails (service will fall back to proxy mode)
2226func NewS3Service(params map[string]any, disablePresigned bool, storageType string) (*S3Service, error) {
···8589 }, nil
8690}
87918888-// blobPath converts a digest (e.g., "sha256:abc123...") or temp path to a storage path
9292+// BlobPath converts a digest (e.g., "sha256:abc123...") or temp path to a storage path
8993// Distribution stores blobs as: /docker/registry/v2/blobs/{algorithm}/{xx}/{hash}/data
9094// where xx is the first 2 characters of the hash for directory sharding
9195// NOTE: Path must start with / for filesystem driver