Source interface. You can add custom sources to back up data from any system that can provide a file tree.
A custom source lives in its own Go module — you do not need to fork or vendor Cloudstic.The
Source contract lives in github.com/cloudstic/cli/pkg/source, which depends on nothing outside the standard library, and the FileMeta / SourceInfo types it is written in are re-exported from the root github.com/cloudstic/cli package. Those re-exports are Go type aliases, so they denote the identical type — a cloudstic.FileMeta you construct in your own module satisfies the interface exactly.Contributing a source upstream is still welcome (see the Contributing Guide), but it is no longer a requirement.Source Interface
All sources must implement theSource interface defined in pkg/source/interface.go:
cloudstic.FileMeta and cloudstic.SourceInfo alias exactly the types the interface is declared with:
Method Descriptions
Walk
ctx- Context for cancellationcallback- Function called for each file/folder, receivesFileMeta
- Error if enumeration fails or callback returns an error
GetFileStream
fileID. The backup engine calls this for each file that needs to be uploaded.
Parameters:
fileID- Source-specific unique identifier (fromFileMeta.FileID)
io.ReadClosercontaining the file data- Error if the file cannot be opened
The caller is responsible for closing the returned reader.
Info
- Find the previous snapshot from the same source (for incremental comparison)
- Group snapshots in retention policies (
forget --group-by source,account,path)
SourceInfowith stable lineage fields (Identity,PathID) and display fields
Size
SourceSizewithBytesandFilescounts- Error if size cannot be determined (implementation can return approximate values)
Existing Sources
Six source types ship today, each in its own subpackage underpkg/source/ — read one of these before writing your own; they’re the reference implementations this guide’s example is modeled on:
Each provider is a separate package so that importing the
Source contract does not drag in a provider’s SDK: pkg/source itself has no third-party dependencies, while only pkg/source/gdrive pulls in the Google API client.
See docs/sources.md in the repository for each type’s exact FileID/Identity/PathID conventions.
Supporting Types
SourceInfo
Older snapshots may have been written before
Identity/PathID existed (or before a source’s volume-UUID-based identity was introduced — see RFC 0005 and RFC 0009). A local source resolves Identity from the detected partition UUID (overridable via CLOUDSTIC_VOLUME_UUID / -volume-uuid, for cross-machine incremental backup of portable drives), falling back to the hostname; PathID is the path relative to the volume’s mount point, so it stays stable if the drive is remounted elsewhere.Type + Identity + PathID(preferred)Type + Identity + Path(bridge fallback, for snapshots written beforePathIDexisted)Type + Account + Path(legacy fallback, for snapshots written before source identity was tracked at all)
FileMeta
FileID- Must be stable and unique within the source. Used as the HAMT key.Type- Must be"file"or"folder"Parents- List of parentFileIDs. Can be empty for root items.ContentHash- If your source provides checksums (like Google Drive), include them here to avoid re-downloading unchanged files. Leave empty if not available.Mode/Uid/Gid/Btime/Flags/Xattrs- POSIX-style attributes added by RFC 0004, used for permission/ownership preservation on restore. Populate them if your source exposes them (thelocalsource does on macOS/Linux); leave the zero value otherwise — a source with no concept of Unix permissions (Google Drive, OneDrive) should simply not set them.ContentRef- Written by the backup engine itself; leave it at its zero value when emittingFileMetafromWalk/WalkChanges.
SourceSize
Example: Simple HTTP Source
Here’s a complete example implementing a source that backs up files from an HTTP server. It lives in your own module — the only Cloudstic imports are the root package (for theFileMeta / SourceInfo aliases) and pkg/source (for SourceSize):
Using the Custom Source
From code elsewhere in the module (or, once merged, from the-source http CLI wiring described below):
Incremental Source Interface
For sources that support delta-based backups (like Google Drive Changes API or OneDrive Delta API), implement theIncrementalSource interface:
IncrementalSource Methods
GetStartPageToken
Walk to capture the baseline state.
Returns:
- Token string to persist in the snapshot
- Error if token cannot be retrieved
WalkChanges
token. Returns the new token to persist for the next run.
Parameters:
ctx- Context for cancellationtoken- Token from the previous snapshot (or fromGetStartPageToken)callback- Function called for each change
newToken- New token to store in the snapshoterr- Error if change enumeration fails
FileChange
ChangeDelete, only Meta.FileID is required. All other fields can be empty.
Example: Incremental HTTP Source
Extending the previous HTTP source to support incremental backups:How the Engine Uses Incremental Sources
- First backup - Engine calls
GetStartPageToken(), thenWalk()for full scan, stores token in snapshot - Subsequent backups - Engine calls
WalkChanges(token)with the stored token, processes only the delta - Token updated - New token from
WalkChangesis stored in the snapshot for the next run
If
WalkChanges fails (e.g., token expired), the engine automatically falls back to a full Walk.Implementation Guidelines
Parent-Child Ordering
The most critical requirement is that parents must be emitted before children. This ensures the HAMT tree structure is built correctly.FileID Stability
FileID must be stable across backups. If a file’s FileID changes, the engine treats it as a deletion + addition rather than a modification.
ContentHash Optimization
If your source provides checksums (like Google Drive’s SHA-256 or OneDrive’s QuickXorHash), include them inContentHash:
Error Handling
Return descriptive errors that help users diagnose issues:Context Cancellation
Respect thecontext.Context passed to Walk, WalkChanges, and Size:
Testing Your Source
Create a simple test to verify your source implementation:Registering Your Source
The CLI doesn’t dispatch source types through a hand-written flag parser.-source is a URI whose scheme selects the source: parseSourceURI (cmd/cloudstic/storeuri.go) parses the scheme and path/host out of the raw string (e.g. local:./documents, sftp://user@host/path, gdrive://Drive Name/path), and initSource (cmd/cloudstic/cmd_backup.go) switches on uri.scheme to construct the matching source.Source with its functional options:
http as a real scheme, extend parseSourceURI’s scheme switch to recognize it (so http://example.com parses into a sourceURIParts{scheme: "http", host: "example.com"}) and add the corresponding case "http": to initSource. Any source-specific flags (credentials, exclude patterns, and so on) are declared like any other flag — see declareBackupArgs in cmd/cloudstic/cmd_backup.go and the CLI Integration section of the Contributing Guide — not parsed ad hoc inside initSource.
Then use it: