# Configuration And Repository Setup LFS Cloud uses two separate configuration scopes: - Private server `config.yml` contains provider credentials, repository routing, networking, and metadata settings. Do not commit it. - Repository `.lfsconfig` contains Git LFS routing and may be committed so collaborators use the same endpoint. ## Server Configuration The default private configuration path is `${HOME}/.config/lfscloud/config.yml`. On Windows it is `%APPDATA%\lfscloud\config.yml`, falling back to `%USERPROFILE%\AppData\Roaming\lfscloud\config.yml` when `APPDATA` is unavailable. Only server administration and runtime commands (`config`, `repository`, `sessions`, and `serve`) read private server configuration. They create a missing file and parent directories; new Unix files use mode `0600` and directories use mode `0700`, while Windows paths inherit their parent directory's access controls. Use global `--config PATH` to select another server file. Client-only commands (`init`, `login`, `logout`, `status`, `pull`, `hydrate`, `dehydrate`, `gc`, and `migrate`) use repository configuration, the local credential helper, and the local cache only. They never read or create private server configuration and reject `--config` rather than silently treating it as client state. Human-readable server and client command output uses semantic colors for headings, statuses, warnings, errors, and key values when its destination is an interactive terminal that supports color. Tracing logs use the same terminal policy. Redirected and piped output remains plain. Set the standard `NO_COLOR` environment variable to a non-empty value, such as `NO_COLOR=1`, to disable colors in a terminal. Automation that allocates a pseudo-terminal should set `NO_COLOR=1` when it needs stable plain text. The README's interactive setup is the shortest route to a valid configuration. With no entry flags, each `add` command prompts for the required values. With entry flags, it becomes non-interactive; an existing ID accepts partial updates, while a new entry requires its non-defaulted fields. Successful writes are validated and atomically replace the prior file. Run the relevant command with `--help` for its accepted fields. The details that are not visible there are: - YAML values may reference `${NAME}`. Every referenced variable must be set whenever the file is loaded or edited. - Google Drive credential directories and client-secret paths entered through interactive setup resolve a leading `~/` against the current user's home directory. - Interactive repository setup uses `gh` to resolve GitHub's immutable numeric repository ID. Set `LFS_CLOUD_GH_EXECUTABLE` when `gh` is outside the normal executable search path. - Configuration writes preserve values and environment references but normalize comments and formatting. - One server process supports one GitHub repository-provider entry and any number of repository mappings that use it. ### Minimal Configuration Interactive setup writes a configuration shaped like this: ```yaml repository_providers: github: type: github storage_providers: google_drive: type: google_drive credentials: type: gcloud config_dir: ${HOME}/.config/lfscloud/gcloud-drive root_folder_id: DRIVE_FOLDER_ID repositories: - id: github:OWNER/REPOSITORY repo_provider: github host: github.com owner: OWNER name: REPOSITORY provider_repository_id: '123456789' storage_provider: google_drive ``` On Windows, generated paths use `${USERPROFILE}` instead of `${HOME}`. `DRIVE_FOLDER_ID` is the actual Google Drive folder ID, not its display name. The repository name omits `.git`. When configuring a mapping without interactive setup, obtain its stable GitHub ID with: ```bash gh api repos/OWNER/REPOSITORY --jq .id ``` GitHub providers use `https://api.github.com` unless `api_url` is set, most commonly for GitHub Enterprise Server. ## Google Drive Create a Desktop OAuth client in a Google Cloud project with the Drive API enabled, download its JSON file, then select it during `lfscloud config storage add`. LFS Cloud creates an isolated Application Default Credentials directory, launches `gcloud` authorization, and creates or reuses an app-owned `.lfscloud` folder by default. It stores the folder's real ID only after authorization and folder validation succeed. The isolated credential needs both scopes when scopes are supplied explicitly: ```text https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/drive.file ``` The `drive.file` scope limits access to app-created or explicitly app-accessible content. A custom `root_folder_id` must identify a folder that the credential can access and create children in; `root` selects the whole My Drive root. Git users never receive Drive tokens or direct Drive access. Keep the Desktop OAuth client JSON available when reauthorizing expired or revoked ADC. Omitting it while updating an existing provider retains the current credential directory and root. Running `gcloud auth application-default login` manually without the project-specific client can use Google's shared OAuth client and later fail Drive quota or API checks. LFS Cloud invokes `gcloud` while the server runs. The default executable is `gcloud` on Unix and `gcloud.cmd` on Windows. If the Windows launcher cannot find an installed Python interpreter, set it explicitly and open a new terminal: ```powershell [Environment]::SetEnvironmentVariable( 'CLOUDSDK_PYTHON', (Get-Command python).Source, 'User' ) ``` Before binding its listener, the server verifies every configured ADC source and confirms that each Drive root is a writable folder. Startup fails instead of reporting readiness when either check fails. ## Network Configuration The default listener is `0.0.0.0:15370`, accepting IPv4 connections through loopback, LAN, and direct Tailscale addresses. Without `server.public_url`, Git LFS action URLs use the actual local destination of each accepted connection; request `Host` and forwarded headers are not trusted. Set `server.public_url` when clients must receive a different hostname, path prefix, or TLS-terminating proxy URL: ```yaml server: public_url: https://lfs.example.com ``` LFS Cloud serves HTTP directly. Non-loopback HTTP client commands require `--allow-insecure-http`, and an explicitly configured non-loopback HTTP `public_url` also requires `server.allow_insecure_http: true`. This is suitable only for a trusted LAN or an encrypted tunnel such as Tailscale; use TLS termination elsewhere. ```yaml server: public_url: http://lfs-host.example.ts.net:15370 allow_insecure_http: true ``` Explicit URLs must not contain credentials, query strings, fragments, trailing slashes, backslashes, or dot path segments. ## Authentication And Sessions `login` verifies the user's GitHub identity but grants no access by itself. Every Git LFS operation checks that user's current permission on the mapped repository: - read or stronger permits downloads - write or admin permits uploads and migration Organization SSO policy, PAT scope, expiry, and revocation still apply. Git's credential helper stores only an opaque repository-scoped LFS Cloud token. The server encrypts the corresponding PAT and session metadata with AES-256-GCM; SQLite stores only the local token's SHA-256 digest. Sessions remain valid across server restarts until logout, definitive GitHub PAT rejection, key rotation, or session-state removal. By default, the encryption key is stored in macOS Keychain, Windows Credential Manager, or Secret Service on Linux and is associated with a non-secret installation ID in the metadata database. A locked, denied, or unavailable credential store prevents startup rather than silently replacing the key. Headless systems without a native credential store can set `server.session_encryption_secret` to an environment reference containing at least 32 characters: ```yaml server: session_encryption_secret: ${LFS_CLOUD_SESSION_SECRET} ``` To replace a managed native-store key, stop the server and run: ```bash lfscloud sessions generate-key ``` Rotation invalidates every active session, so users must log in again. It is available only while the server is stopped and the native credential store manages the key. ## Repository Setup Run `init`, `login`, and `status` from a Git repository already present in the server's repository mappings. `init` derives the repository identity from its Git remote and writes the matching endpoint to `.lfsconfig`: ```ini [lfs] url = http://127.0.0.1:15370/github.com/octo-org/assets.git/info/lfs ``` Commit `.lfsconfig` when collaborators should share the endpoint. Use `init --local` instead when routing must remain local to one checkout. After initialization, client commands infer the server only when the configured URL exactly matches the current Git remote's LFS Cloud route; an explicit server remains available as an override. `status` checks the local repository identity, configured LFS Cloud route, server reachability, repository-scoped credential, and shared-cache readiness. Server mappings, provider credentials, and storage readiness remain private to the server and are validated by server startup rather than by clients. Every machine that runs `login` needs a Git credential helper. Git for Windows normally configures Git Credential Manager; macOS can use `osxkeychain`; Linux users may need a secure helper such as `libsecret`. `login` reports platform-appropriate setup instructions when none is configured. Because `.lfsconfig` can be committed, review it before authenticating from an untrusted clone. ## Migration Migration is server-mediated: the client inventories Git LFS pointers, asks LFS Cloud which objects are missing, fetches only those bytes from the existing LFS endpoint, and uploads them through server-issued actions. The server performs GitHub write-permission checks and all Google Drive access. The client never reads private server configuration or Drive credentials. An executing migration requires a non-shallow repository and Git 2.40 or newer. It refreshes the selected source remote's branches and tags, then scans local branches, tags, and the fetched source refs. Smaller current-checkout or selected-ref scopes are available for dry-run investigation, while execution requires the complete `--all-refs` inventory. For an initial migration, pass the LFS Cloud address explicitly because the repository's effective LFS URL still names the source provider. After success, client commands can infer LFS Cloud from the saved target route. Configuration changes are transactional. Only after every object succeeds does migration write the target to both `.lfsconfig` and repository-local `lfs.url`; it also preserves the prior endpoint as `remote..lfsurl` in `.lfsconfig`. That remote-scoped URL remains dormant during normal traffic and lets later collaborators migrate objects that only they can access. URLs containing credentials, queries, or fragments are never committed. Retries reconcile the inventory with the server and skip objects already uploaded by this or another user. If `.lfsconfig` already names LFS Cloud, migration ignores that target as a source and falls back to the preserved remote URL or the selected Git remote's default LFS endpoint. Selecting another address for the same LFS Cloud route, such as loopback instead of Tailscale, does not turn the target into a source. Each target-missing upload shows its sequence, OID, transferred and total bytes, and current transfer speed. The active status refreshes in place at most ten times per second, then becomes a completed line with the object size and a checkmark. A failed upload leaves its current status in place before the terminating error. Target-present objects appear only in the final summary. Migration never rewrites Git history or deletes source objects automatically. ## Resource Limits Optional server settings bound work across the process: | Setting | Default | Effect | | --------------------------------- | ------: | ----------------------------------------------------- | | `max_batch_objects` | 100 | Objects accepted in one Git LFS batch request | | `max_provider_calls` | 16 | Concurrent GitHub and storage-provider calls | | `max_concurrent_requests` | 64 | Active HTTP requests | | `max_concurrent_uploads` | 8 | Uploads retaining staging disk and provider resources | | `max_concurrent_uploads_per_user` | 2 | Staged uploads owned by one provider user | Excess HTTP or upload work receives HTTP 503 with `Retry-After: 1`. Uploads also reserve their declared size against temporary-directory capacity and retain 64 MiB of free-space headroom. All configured limits must be positive, and the per-user upload limit cannot exceed the process-wide upload limit. ## Metadata Without `server.metadata_path`, SQLite metadata is stored at `/.lfscloud/metadata.sqlite3`. Relative paths resolve from the configuration directory. The server creates a lifecycle lock and an `upload-locks` directory beside the database. One installation supports one active server process; the object locks preserve upload serialization across restarts. Multiple hosts sharing one metadata installation are not supported.