Giftless Git LFS Server

repository·main·Indexed 41 days ago

https://github.com/datopian/giftless

A pluggable Git LFS server implementation in Python. Giftless allows developers to customize storage backends (including Amazon S3, Azure Blob Storage, Google Cloud Storage, and local storage), transfer methods, and authentication providers such as JWT and GitHub.

Tokens
18.3K
Snippets
67
Records
83
Agent score
51%

What's inside Giftless

  1. Overview of Giftless Git LFS Server

    main

    Giftless is a Python-based implementation of a Git LFS (Large File Storage) server. It is designed to be highly flexible through a pluggable architecture that allows users to customize storage backends, transfer methods, and authentication methods.

    Supported Storage Backends

    Giftless supports the standard Git LFS transfer mode with the following backends:

    • Local storage
    • Google Cloud Storage
    • Azure Blob Storage (supports both direct-to-cloud and streamed transfers)
    • Amazon S3 Storage

    Transfer Modes

    • Basic Git LFS: The standard transfer mode supported by most Git LFS clients.
    • multipart-basic: A custom transfer mode designed to leverage multipart upload capabilities from various cloud vendors. Note that this mode requires a specialized client and is not supported by standard Git LFS clients. For a compatible Python client, use giftless-client.
  2. Giftless development standards and dependencies

    main

    Giftless is built on Flask and utilizes the following libraries:

    • Flask Classful: For simplifying API endpoint implementation.
    • Marshmallow: For input/output serialization and validation.
    • figcan: For configuration handling.

    Code Quality Tools:

    • flake8: PEP8 compliance.
    • isort: Import organization.
    • mypy: Type checking.
    • Max Line Length: 120 characters.
  3. How direct GCS transfers work in Giftless

    main

    When using the basic_external transfer adapter with GCS, Giftless acts as a gateway rather than a file server:

    1. Upload: When a Git LFS client requests an upload, Giftless provides a pre-signed GCP URL. The client then uploads the file directly to GCS using that URL. Giftless logs will show the batch and verify requests, but not the PUT request itself.
    2. Download: When a client requests a file, Giftless provides access information, and the file is pulled directly from GCS.

    This approach is highly recommended for production workloads and large files as it reduces the load on the Giftless server.

  4. How Giftless architecture and components work

    main

    Giftless is designed with a highly modular architecture. It allows developers to customize server behavior by implementing or providing specialized classes that handle specific responsibilities. The core extensibility points are organized into three main categories:

    1. Transfer Adapters: Handle the logic for how data is transferred.
    2. Storage Backends: Manage how and where the actual LFS objects are persisted.
    3. Auth Providers: Control the authentication and authorization logic for incoming requests.

    By swapping these components, you can adapt Giftless to different network protocols, storage infrastructures (like S3 or local disk), and identity management systems.

  5. How storage backends and transfer adapters work together

    main

    In Giftless, Storage Backend classes manage the actual file storage (e.g., local filesystem or cloud providers). Transfer Adapters define how clients interact with these files.

    Storage backends implement specific interfaces that determine which transfer adapters they can work with:

    • StreamingStorage: Supports streaming object upload/download via the Giftless HTTP server. Compatible with the basic_streaming transfer adapter.
    • ExternalStorage: Allows referring clients to upload/download objects using an external HTTP server. Compatible with the basic_external transfer adapter. Typically used with Cloud Storage.
    • MultipartStorage: Supports the multipart-basic transfer mode. Typically used with Cloud Storage.
    • VerifiableStorage: Provides APIs to verify that an object was uploaded correctly. Most concrete adapters implement this.

    To use a backend, you must configure it within the options of a specific transfer adapter in your giftless.yaml file.

    TRANSFER_ADAPTERS:
      basic:  # The name of the transfer mode
        factory: giftless.transfer.basic_external:factory
        options:
          storage_class: giftless.storage.google_cloud:GoogleCloudStorage
          storage_options:
            # Backend-specific settings go here
  6. Use Scopes for JWT Authorization

    main

    JWT tokens can include an authorization payload in the scopes claim. Scopes follow a specific pattern to grant access to specific objects, repositories, or organizations.

    Scope Pattern: obj:{org}/{repo}/{oid}:{subscope}:{actions}

    Components:

    • {org}: The organization name.
    • {repo}: The repository name. Use * to grant access to all repositories in the organization.
    • {oid}: The Object ID. Use * to grant access to all objects in the repository.
    • {subscope}: Optional. Use metadata to grant access to metadata only (e.g., verifying existence without downloading files).
    • {actions}: A comma-separated list of read, write, or verify. Use * for all actions.

    Scope Examples

    {
      "scopes": [
        // read a specific object
        "obj:datopian/somerepo/6adada03e86b154be00e25f288fcadc27aef06c47f12f88e3e1985c502803d1b:read",
    
        // read the same object, but do not limit to a specific prefix
        "obj:6adada03e86b154be00e25f288fcadc27aef06c47f12f88e3e1985c502803d1b:read",
    
        // full access to all objects in a repo
        "obj:datopian/my-repo/*",
    
        // Read only access to all repositories for an organization
        "obj:datopian/*:read",
    
        // Metadata read only access to all objects in a repository
        "obj:datopian/my-repo:meta:verify"
      ]
    }
    {
      "scopes": [
        "obj:datopian/somerepo/6adada03e86b154be00e25f288fcadc27aef06c47f12f88e3e1985c502803d1b:read",
        "obj:6adada03e86b154be00e25f288fcadc27aef06c47f12f88e3e1985c502803d1b:read",
        "obj:datopian/my-repo/*",
        "obj:datopian/*:read",
        "obj:datopian/my-repo:meta:verify"
      ]
    }
  7. How Authenticators and Identities work in Giftless

    main

    Giftless uses a two-step process for security:

    1. Authenticators: Subclasses of giftless.auth.Authenticator that attempt to extract a user identity from an HTTP request. Multiple authenticators can be chained; if one fails, the next is tried. If all fail, a 401 Unauthorized is returned by default.
    2. Identity: An object representing the authenticated user. It contains user metadata (name, email, id) and provides the is_authorized method used by Authorizers to check permissions.

    When implementing a custom authenticator, you must return an Identity object that implements the is_authorized interface.

    from typing import Optional
    from giftless.auth.identity import Permission
    
    class Identity:
        name: Optional[str] = None
        id: Optional[str] = None
        email: Optional[str] = None
    
        def is_authorized(
            self, 
            organization: str, 
            repo: str, 
            permission: Permission, 
            oid: Optional[str] = None
        ) -> bool:
            """Tell if user is authorized to perform an operation on an object / repo"""
            pass
  8. Terminology for multipart-basic transfer mode

    main

    When working with the multipart-basic protocol, the following roles are defined:

    • LFS Server: The HTTP server that receives the initial LFS batch request.
    • Client (or LFS Client): The client using the Git LFS protocol to push large files to storage via the LFS server.
    • Storage Backend: The HTTP server handling the actual file storage. This is typically a cloud storage service (e.g., Amazon S3, Google Cloud Storage) and is often a different entity than the LFS Server.
  9. Understand Authentication vs Authorization in Giftless

    main

    Giftless distinguishes between two core security concepts:

    • Authentication (authn): Validating the identity of the entity (person or machine) sending a request.
    • Authorization (authz): Deciding if the established identity is permitted to perform a specific operation.

    In Giftless, these are often handled together by 'auth modules', but they are conceptually separate.

  10. Understand the Git LFS `multipart-basic` transfer mode

    main

    The multipart-basic transfer mode is an extension to the standard Git LFS basic protocol that allows for chunked file uploads.

    Key Characteristics

    • Compatibility: Clients must include basic in the transfers list (e.g., ["multipart-basic", "basic"]) to ensure fallback if the server doesn't support this mode.
    • Download: download operations behave identically to the basic protocol.
    • Upload Lifecycle: Unlike the single-step basic upload, multipart-basic breaks uploads into several optional actions:
      • init: Initialize the upload.
      • parts: Zero or more requests to upload specific file chunks.
      • commit: Finalize the upload.
      • abort: Cancel the upload and clean up state.
      • verify: Verify the file exists in storage.
    • Skipping Uploads: If a file is already fully committed to storage, the server will return a response with no actions, allowing the client to skip the upload entirely.
    {
      "transfers": ["multipart-basic", "basic"],
      "operation": "upload",
      "objects": [
        {
          "oid": "20492a4d0d84f8beb1767f6616229f85d44c2827b64bdbfb260ee12fa1109e0e",
          "size": 10000000
        }
      ]
    }
  11. Choose between `basic_external` and `basic_streaming` transfer adapters

    main

    Giftless supports the standard Git LFS basic transfer mode via two different adapters, depending on whether you want Giftless to handle the data or let the client talk directly to storage:

    1. basic_external (Recommended for Cloud Storage): Files are uploaded or downloaded directly from a storage service (supporting HTTP PUT/GET) without passing through Giftless. Giftless only provides the client with access credentials. This requires a storage adapter that implements the ExternalStorage interface.
    2. basic_streaming (For local or specific cloud storage): Giftless handles all object upload, download, and verification requests directly. This is less scalable because all data passes through the Giftless Python runtime, but it is useful for local storage or specific cloud backends like Azure and Google Cloud when direct access isn't preferred.
  12. Install and run Giftless locally

    main

    To run Giftless locally for development, ensure you have Python 3.7+ installed. It is recommended to use a virtual environment. You can run Giftless using Flask's built-in development server by setting the FLASK_APP environment variable to giftless.wsgi_entrypoint.

    Note: The Flask development server is not suitable for production use.

    # Create and activate virtual environment
    mkdir giftless && cd giftless
    python -m venv .venv
    source .venv/bin/activate
    
    # Run Giftless using the built-in development server
    export FLASK_APP=giftless.wsgi_entrypoint
    flask run