Documentation
¶
Overview ¶
Package lfs implements the Git LFS Batch API protocol, including HTTP handlers, HMAC-signed URL generation, and file locking.
Index ¶
- Variables
- type Action
- type BatchRequest
- type BatchResponse
- type CreateLockRequest
- type CreateLockResponse
- type ErrorResponse
- type FileLockStore
- func (s *FileLockStore) Create(ctx context.Context, endpoint, path, ownerName string) (Lock, error)
- func (s *FileLockStore) List(ctx context.Context, endpoint string, opts ListLocksOptions) ([]Lock, string, error)
- func (s *FileLockStore) Unlock(ctx context.Context, endpoint, id, requester string, force bool) (Lock, error)
- type Handler
- func (h *Handler) BatchHandler(w http.ResponseWriter, r *http.Request)
- func (h *Handler) CreateLockHandler(w http.ResponseWriter, r *http.Request)
- func (h *Handler) DownloadHandler(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListLocksHandler(w http.ResponseWriter, r *http.Request)
- func (h *Handler) UnlockHandler(w http.ResponseWriter, r *http.Request)
- func (h *Handler) UploadHandler(w http.ResponseWriter, r *http.Request)
- func (h *Handler) VerifyHandler(w http.ResponseWriter, r *http.Request)
- func (h *Handler) VerifyLocksHandler(w http.ResponseWriter, r *http.Request)
- type HandlerConfig
- type ListLocksOptions
- type ListLocksResponse
- type Lock
- type LockOwner
- type LockStore
- type Object
- type ObjectError
- type ObjectResponse
- type Ref
- type SignedURL
- type URLSigner
- type UnlockRequest
- type UnlockResponse
- type VerifyLocksRequest
- type VerifyLocksResponse
- type VerifyRequest
Constants ¶
This section is empty.
Variables ¶
var ( ErrLockExists = errors.New("lock already exists") ErrLockNotFound = errors.New("lock not found") ErrLockNotOwner = errors.New("not the lock owner") )
Sentinel errors for lock operations.
Functions ¶
This section is empty.
Types ¶
type Action ¶
type Action struct {
Href string `json:"href"`
Header map[string]string `json:"header,omitempty"`
ExpiresIn int64 `json:"expires_in,omitempty"`
ExpiresAt string `json:"expires_at,omitempty"`
}
Action describes an LFS transfer action (download, upload, verify).
type BatchRequest ¶
type BatchRequest struct {
Operation string `json:"operation"`
Transfers []string `json:"transfers,omitempty"`
Ref *Ref `json:"ref,omitempty"`
Objects []Object `json:"objects"`
HashAlgo string `json:"hash_algo,omitempty"`
}
BatchRequest is the incoming request to POST /objects/batch.
type BatchResponse ¶
type BatchResponse struct {
Transfer string `json:"transfer,omitempty"`
Objects []ObjectResponse `json:"objects"`
HashAlgo string `json:"hash_algo,omitempty"`
}
BatchResponse is the response from POST /objects/batch.
type CreateLockRequest ¶
CreateLockRequest is the incoming request to POST /locks.
type CreateLockResponse ¶
type CreateLockResponse struct {
Lock Lock `json:"lock"`
Message string `json:"message,omitempty"`
RequestID string `json:"request_id,omitempty"`
DocumentationURL string `json:"documentation_url,omitempty"`
}
CreateLockResponse is the response from POST /locks.
type ErrorResponse ¶
type ErrorResponse struct {
Message string `json:"message"`
RequestID string `json:"request_id,omitempty"`
DocumentationURL string `json:"documentation_url,omitempty"`
}
ErrorResponse is a top-level error response.
type FileLockStore ¶
type FileLockStore struct {
// contains filtered or unexported fields
}
FileLockStore persists locks as JSON files on the local filesystem. An in-memory cache avoids repeated disk reads; it is validated against the on-disk file's modification time and size on every access, so external modifications (manual edits, restores) are detected automatically.
Write operations (Create, Unlock) load and rewrite the entire lock list, making this implementation O(n) per mutation. This is well-suited for typical LFS deployments with moderate lock counts (hundreds to low thousands per endpoint). Deployments expecting very high lock volumes should consider a database-backed LockStore implementation.
func NewFileLockStore ¶
func NewFileLockStore(basePath string) *FileLockStore
NewFileLockStore creates a FileLockStore rooted at basePath.
func (*FileLockStore) List ¶
func (s *FileLockStore) List(ctx context.Context, endpoint string, opts ListLocksOptions) ([]Lock, string, error)
List returns locks matching the given options with cursor-based pagination.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler serves Git LFS API requests for a single endpoint.
func NewHandler ¶
func NewHandler(cfg HandlerConfig) *Handler
NewHandler creates a Handler for the given configuration.
func (*Handler) BatchHandler ¶
func (h *Handler) BatchHandler(w http.ResponseWriter, r *http.Request)
BatchHandler handles POST /objects/batch requests.
func (*Handler) CreateLockHandler ¶
func (h *Handler) CreateLockHandler(w http.ResponseWriter, r *http.Request)
CreateLockHandler handles POST /{endpoint}/locks requests.
func (*Handler) DownloadHandler ¶
func (h *Handler) DownloadHandler(w http.ResponseWriter, r *http.Request)
DownloadHandler handles GET /objects/{oid} requests.
The HMAC-signed URL already proves the batch handler authenticated and authorized this request. The batch response sets "authenticated": true, which tells the Git LFS client not to resend credentials on transfer requests.
func (*Handler) ListLocksHandler ¶
func (h *Handler) ListLocksHandler(w http.ResponseWriter, r *http.Request)
ListLocksHandler handles GET /{endpoint}/locks requests. Only Accept is validated (not Content-Type) because GET requests have no body.
func (*Handler) UnlockHandler ¶
func (h *Handler) UnlockHandler(w http.ResponseWriter, r *http.Request)
UnlockHandler handles POST /{endpoint}/locks/{id}/unlock requests.
func (*Handler) UploadHandler ¶
func (h *Handler) UploadHandler(w http.ResponseWriter, r *http.Request)
UploadHandler handles PUT /objects/{oid} requests.
The HMAC-signed URL already proves the batch handler authenticated and authorized this request. The batch response sets "authenticated": true, which tells the Git LFS client not to resend credentials on transfer requests.
func (*Handler) VerifyHandler ¶
func (h *Handler) VerifyHandler(w http.ResponseWriter, r *http.Request)
VerifyHandler handles POST /objects/verify requests.
func (*Handler) VerifyLocksHandler ¶
func (h *Handler) VerifyLocksHandler(w http.ResponseWriter, r *http.Request)
VerifyLocksHandler handles POST /{endpoint}/locks/verify requests.
type HandlerConfig ¶
type HandlerConfig struct {
Endpoint *config.Endpoint
Store storage.Adapter
Auth auth.Authenticator
BaseURL string
MaxUploadSize int64
Signer *URLSigner
LockStore LockStore
}
HandlerConfig holds the dependencies for creating a Handler.
type ListLocksOptions ¶
type ListLocksOptions struct {
Path string
ID string
Cursor string
Limit int
Refspec string // Accepted per spec; not used for filtering (auth is repo-level).
}
ListLocksOptions configures lock listing queries.
type ListLocksResponse ¶
type ListLocksResponse struct {
Locks []Lock `json:"locks"`
NextCursor string `json:"next_cursor,omitempty"`
}
ListLocksResponse is the response from GET /locks.
type Lock ¶
type Lock struct {
ID string `json:"id"`
Path string `json:"path"`
LockedAt string `json:"locked_at"`
Owner LockOwner `json:"owner"`
}
Lock represents a file lock.
type LockOwner ¶
type LockOwner struct {
Name string `json:"name"`
}
LockOwner identifies who holds a lock.
type LockStore ¶
type LockStore interface {
// Create acquires a lock on the given path. Returns ErrLockExists if the
// path is already locked (the existing lock is returned alongside the error).
Create(ctx context.Context, endpoint, path, ownerName string) (Lock, error)
// List returns locks matching the given options with cursor-based pagination.
// The returned string is the next cursor (empty if no more results).
List(ctx context.Context, endpoint string, opts ListLocksOptions) ([]Lock, string, error)
// Unlock releases a lock. Returns ErrLockNotFound if the lock does not
// exist, or ErrLockNotOwner if the requester is not the owner and force
// is false.
Unlock(ctx context.Context, endpoint, id, requester string, force bool) (Lock, error)
}
LockStore manages file locks for LFS endpoints.
type ObjectError ¶
ObjectError is an error attached to a single object in a batch response.
type ObjectResponse ¶
type ObjectResponse struct {
OID string `json:"oid"`
Size int64 `json:"size"`
Authenticated *bool `json:"authenticated,omitempty"`
Actions map[string]Action `json:"actions,omitempty"`
Error *ObjectError `json:"error,omitempty"`
}
ObjectResponse is the per-object response within a batch response.
Authenticated is a pointer so the field is omitted from JSON when nil (object errors) but serialised as true/false when set (successful actions). The LFS spec uses this to tell the client whether the server already authenticated the action URLs, avoiding redundant credential prompts.
type Ref ¶
type Ref struct {
Name string `json:"name"`
}
Ref identifies a Git ref associated with a batch request. The spec allows servers to use this for ref-aware authorization (e.g., per-branch permissions). Anchor LFS authorises at the repository level via GitHub permissions, so the ref is accepted for protocol compliance but not used for access decisions.
type SignedURL ¶
SignedURL holds a signed URL together with its expiration metadata, all derived from a single timestamp to avoid clock skew between fields.
type URLSigner ¶
type URLSigner struct {
// contains filtered or unexported fields
}
URLSigner produces and validates HMAC-signed URLs with expiration.
func NewURLSigner ¶
NewURLSigner creates a signer. If key is nil or empty, a random 32-byte key is generated in memory (callers should prefer persisting the key to disk).
func (*URLSigner) Close ¶
Close zeros the signing key in memory. It should be called during graceful shutdown to reduce the window in which key material is recoverable from a process memory dump.
func (*URLSigner) Sign ¶
Sign returns a signed URL with expiration query parameters appended. The baseURL is prepended to the signed path for the final href; the MAC is computed over the path and expiry only (not the scheme/host). The returned SignedURL includes expiration metadata derived from the same timestamp used for signing, ensuring consistency across all fields.
func (*URLSigner) Verify ¶
Verify checks the signature and expiration of a request URI (path + query). Returns nil if valid.
The MAC is computed over "path?exp=<value>" only — the sig parameter and any other query parameters are excluded from the signed message. This means additional query parameters will not invalidate the signature, but they also will not be covered by it. Do not rely on unsigned query parameters for security-sensitive decisions.
type UnlockRequest ¶
UnlockRequest is the incoming request to POST /locks/:id/unlock.
type UnlockResponse ¶
type UnlockResponse struct {
Lock Lock `json:"lock"`
}
UnlockResponse is the response from POST /locks/:id/unlock.
type VerifyLocksRequest ¶
type VerifyLocksRequest struct {
Ref *Ref `json:"ref,omitempty"`
Cursor string `json:"cursor,omitempty"`
Limit int `json:"limit,omitempty"`
}
VerifyLocksRequest is the incoming request to POST /locks/verify.
type VerifyLocksResponse ¶
type VerifyLocksResponse struct {
Ours []Lock `json:"ours"`
Theirs []Lock `json:"theirs"`
NextCursor string `json:"next_cursor,omitempty"`
}
VerifyLocksResponse is the response from POST /locks/verify.
type VerifyRequest ¶
VerifyRequest is the incoming request to POST /objects/verify.