Skip to main content
Cloudstic supports multiple data sources through a unified 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 the Source interface defined in pkg/source/interface.go:
From outside the module you spell those two types through the root package — cloudstic.FileMeta and cloudstic.SourceInfo alias exactly the types the interface is declared with:

Method Descriptions

Walk

Enumerates every file and folder in the source. Parent folders MUST be emitted before their children to ensure the HAMT tree structure is built correctly. Parameters:
  • ctx - Context for cancellation
  • callback - Function called for each file/folder, receives FileMeta
Returns:
  • Error if enumeration fails or callback returns an error

GetFileStream

Returns a readable stream for a file identified by its source-specific fileID. The backup engine calls this for each file that needs to be uploaded. Parameters:
  • fileID - Source-specific unique identifier (from FileMeta.FileID)
Returns:
  • io.ReadCloser containing the file data
  • Error if the file cannot be opened
The caller is responsible for closing the returned reader.

Info

Returns metadata about the source. This is stored in the snapshot and used to:
  • Find the previous snapshot from the same source (for incremental comparison)
  • Group snapshots in retention policies (forget --group-by source,account,path)
Returns:
  • SourceInfo with stable lineage fields (Identity, PathID) and display fields

Size

Returns the total size of the source. This is used for progress reporting during backup. Returns:
  • SourceSize with Bytes and Files counts
  • Error if size cannot be determined (implementation can return approximate values)

Existing Sources

Six source types ship today, each in its own subpackage under pkg/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.
Snapshots from the same source are matched against the previous snapshot in three passes, falling back only when the more specific match is unavailable:
  1. Type + Identity + PathID (preferred)
  2. Type + Identity + Path (bridge fallback, for snapshots written before PathID existed)
  3. Type + Account + Path (legacy fallback, for snapshots written before source identity was tracked at all)

FileMeta

Important fields:
  • FileID - Must be stable and unique within the source. Used as the HAMT key.
  • Type - Must be "file" or "folder"
  • Parents - List of parent FileIDs. 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 (the local source 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 emitting FileMeta from Walk/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 the FileMeta / 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 the IncrementalSource interface:

IncrementalSource Methods

GetStartPageToken

Returns an opaque token representing the current head of the change stream. The engine calls this before the first full Walk to capture the baseline state. Returns:
  • Token string to persist in the snapshot
  • Error if token cannot be retrieved

WalkChanges

Emits only the entries that changed since token. Returns the new token to persist for the next run. Parameters:
  • ctx - Context for cancellation
  • token - Token from the previous snapshot (or from GetStartPageToken)
  • callback - Function called for each change
Returns:
  • newToken - New token to store in the snapshot
  • err - Error if change enumeration fails

FileChange

For 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

  1. First backup - Engine calls GetStartPageToken(), then Walk() for full scan, stores token in snapshot
  2. Subsequent backups - Engine calls WalkChanges(token) with the stored token, processes only the delta
  3. Token updated - New token from WalkChanges is 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.
For sources with complex hierarchies, use topological sorting to ensure correct ordering.

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 in ContentHash:
This allows the engine to skip downloading unchanged files during incremental backups.

Error Handling

Return descriptive errors that help users diagnose issues:

Context Cancellation

Respect the context.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:
To add 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: