How to Add a New Storage Driver to CasaOS: A Complete Implementation Guide
To add a new storage driver to CasaOS, create a Go package that implements the driver.Driver interface, register it with op.RegisterDriver in an init function, and import the package in drivers/all.go to activate the backend.
CasaOS uses a modular plugin system to support diverse storage backends. When you add a new storage driver to CasaOS, you are implementing a Go package that conforms to the internal driver interfaces and hooks into the registry defined in internal/op. This extensible architecture allows the system to discover and initialize your driver at runtime through side-effect imports.
Understanding the CasaOS Driver Architecture
The driver system relies on two core packages: internal/driver defines the interface contracts, while internal/op maintains the constructor registry. According to the CasaOS source code, every driver must implement the driver.Driver interface, which embeds Meta, Reader, and optional operation interfaces like Mkdir or Move. The registry in internal/op/driver.go stores constructor functions in driverNewMap, mapping driver names to factory functions that return new instances.
When CasaOS starts, it imports drivers/all.go, which performs side-effect imports of all driver packages. Each driver package contains an init function that calls op.RegisterDriver, inserting the constructor into the global registry. This design decouples driver implementation from the core system, allowing new backends to be added without modifying central orchestration logic.
Step-by-Step Implementation Guide
Create the Driver Package
Start by creating a new directory under drivers/, such as drivers/x/. The package must import the internal driver abstractions to access the interface definitions and registration functions.
import (
"github.com/IceWhaleTech/CasaOS/internal/driver"
"github.com/IceWhaleTech/CasaOS/internal/op"
)
This setup links your code to the driver.Config struct and the op.RegisterDriver function used in subsequent steps.
Define the Storage Struct and Configuration
Create a struct that embeds model.StorageA to inherit common storage fields and define an Addition struct for driver-specific configuration. The Addition struct typically includes fields like API keys, endpoints, or OAuth credentials.
type X struct {
model.StorageA // common storage fields
Addition // driver-specific configuration
AccessToken string // runtime token, if needed
}
type Addition struct {
driver.RootID
ClientID string `json:"client_id" required:"true" omit:"true"`
ClientSecret string `json:"client_secret" required:"true" omit:"true"`
}
Define a package-level config variable of type driver.Config that describes the driver's capabilities. As implemented in drivers/google_drive/meta.go, this configuration specifies the driver name, proxy requirements, and default root path.
var config = driver.Config{
Name: "X",
OnlyProxy: true,
DefaultRoot: "root",
}
Implement the Required Interfaces
You must implement several interfaces defined in internal/driver/driver.go:
Meta Interface: Implement Config() driver.Config, GetStorage() *model.StorageA, SetStorage(model.StorageA), GetAddition() driver.Additional, Init(context.Context) error, and Drop(context.Context) error. The Init method handles authentication and setup, while Drop handles cleanup.
Reader Interface: Implement List(context.Context, model.Obj, model.ListArgs) ([]model.Obj, error) to return directory contents, and Link(context.Context, model.Obj, model.LinkArgs) (*model.Link, error) to generate download URLs.
Optional Operation Interfaces: Implement MakeDir, Move, Rename, Copy, Remove, or Put only if your storage backend supports those operations. These are defined as separate interfaces in internal/driver/driver.go.
Register the Driver with the Internal Registry
Add an init function that calls op.RegisterDriver, passing a constructor function that returns a new instance of your driver. The registry links this constructor to the name specified in config.Name.
func init() {
op.RegisterDriver(func() driver.Driver { return &X{} })
}
This registration populates the driverNewMap in internal/op/driver.go, making your driver available to op.GetDriverNames() and op.GetDriverInfoMap().
Expose the Driver to the Build System
Import your driver package in drivers/all.go using a blank import to ensure the init function executes during program startup.
import (
_ "github.com/IceWhaleTech/CasaOS/drivers/x"
// existing imports …
)
This side-effect import triggers the registration chain without exposing the package's symbols directly.
Add Driver Assets (Optional)
If your driver requires a UI icon, reference the asset path in your Addition struct (e.g., Icon: "./img/driver/X.svg") and store the SVG file under static/img/driver/. This allows the CasaOS frontend to display the correct branding for your storage backend.
Complete Code Example
Below is a minimal implementation skeleton for a hypothetical driver X located in drivers/x/drive.go:
package x
import (
"context"
"net/http"
"github.com/IceWhaleTech/CasaOS/internal/driver"
"github.com/IceWhaleTech/CasaOS/internal/op"
"github.com/IceWhaleTech/CasaOS/model"
)
type X struct {
model.StorageA
Addition
AccessToken string
}
// ---- Meta ---------------------------------------------------------
func (d *X) Config() driver.Config { return config }
func (d *X) GetAddition() driver.Additional { return &d.Addition }
func (d *X) Init(ctx context.Context) error { /* obtain token, etc. */ return nil }
func (d *X) Drop(ctx context.Context) error { return nil }
// ---- Reader -------------------------------------------------------
func (d *X) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
// fetch file list from the remote service
return nil, nil
}
func (d *X) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
// build a download URL
return &model.Link{
Method: http.MethodGet,
URL: "https://example.com/download/" + file.GetID(),
}, nil
}
// ---- User ---------------------------------------------------------
func (d *X) GetUserInfo(ctx context.Context) (string, error) { return "user@example.com", nil }
func (d *X) GetInfo(ctx context.Context) (string, string, string, error) { return "", "", "", nil }
// ---- Registration -------------------------------------------------
func init() {
op.RegisterDriver(func() driver.Driver { return &X{} })
}
Accompany this with the configuration file drivers/x/meta.go:
package x
import "github.com/IceWhaleTech/CasaOS/internal/driver"
type Addition struct {
driver.RootID
ClientID string `json:"client_id" required:"true" omit:"true"`
ClientSecret string `json:"client_secret" required:"true" omit:"true"`
}
var config = driver.Config{
Name: "X",
OnlyProxy: true,
DefaultRoot: "root",
}
Key Source Files and Interfaces
Understanding the following files is essential when you add a new storage driver to CasaOS:
internal/op/driver.go: Contains the registry implementation includingRegisterDriver,GetDriverNew, andGetDriverInfoMap.internal/driver/driver.go: Defines the core interfacesDriver,Meta,Reader,User, and optional operation interfaces likeMkdirandMove.drivers/google_drive/drive.go: Reference implementation showing a complete driver with OAuth and API integration.drivers/google_drive/meta.go: Demonstrates properAdditionstruct tagging anddriver.Configdefinition.model/storage.go: Containsmodel.StorageA, the base struct embedded by all driver implementations.drivers/all.go: The central import file that triggers driver registration via side-effect imports.
Testing and Verification
After implementing your driver, run go test ./... to ensure your code compiles and passes existing unit tests. Verify that op.GetDriverNames() returns your driver name and that op.GetDriverInfoMap() contains the configuration fields from your Addition struct. Test the driver's lifecycle by initializing it through the CasaOS API, checking that Init properly authenticates and that List and Link return valid data structures.
Summary
- Create a package under
drivers/that importsinternal/driverandinternal/op. - Embed
model.StorageAand define anAdditionstruct for configuration, using tags likejson:"client_id" required:"true". - Implement
MetaandReaderinterfaces frominternal/driver/driver.go, plus optional operation interfaces as needed. - Register via
initby callingop.RegisterDriverwith a constructor function. - Import in
drivers/all.gousing a blank import to trigger registration at startup. - Reference existing drivers like Google Drive in
drivers/google_drive/as working templates for your implementation.
Frequently Asked Questions
What is the minimum interface required to implement a CasaOS storage driver?
At minimum, you must implement the Meta interface (Config, GetStorage, SetStorage, GetAddition, Init, Drop) and the Reader interface (List, Link) defined in internal/driver/driver.go. These allow CasaOS to initialize the driver, retrieve configuration, and perform basic read operations. Additional interfaces like Mkdir or Move are only required if your backend supports those operations.
How does CasaOS discover new drivers at runtime?
CasaOS discovers drivers through the registry in internal/op/driver.go. When you add a blank import of your driver package to drivers/all.go, Go executes the package's init function during program startup. This function calls op.RegisterDriver, which stores a constructor in the driverNewMap, making the driver available to the rest of the system via op.GetDriverNames().
Can I add a storage driver without modifying CasaOS core files?
Yes. You only need to create a new package under drivers/ and add a single import line to drivers/all.go. You do not need to modify internal/op/driver.go or any other core orchestration code. The driver system is designed to be extensible through side-effect imports and the registration pattern, keeping your changes isolated to your new driver package and the import list.
Where should I store driver-specific assets like icons?
Store SVG or image assets in static/img/driver/ (or the equivalent location in your CasaOS build) and reference them in your Addition struct using a relative path like "./img/driver/X.svg". The CasaOS frontend uses this path to display the driver icon in the storage management UI.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →