Skip to content

Filesystem configuration

config/filesystems.py defines BootstrapFilesystems, an immutable dataclass extending Filesystems. The Storage service reads this configuration when resolved; it constructs and caches each disk on first selection.

FieldType and defaultEffect
defaultDiskName | str; FILESYSTEM_DISK, default local.Disk selected by Storage.disk() and Storage.default().
disksDisks | dict; Disks(...) factory.Configuration for the five storage entries.

DiskName contains LOCAL, PUBLIC, S3, AZURE, and GCS, with values local, public, s3, azure, and gcs. Strings in default are trimmed and normalized: " S3 " becomes "s3". Other names raise ValueError; types other than a string or DiskName raise TypeError.

FILESYSTEM_DISK=s3

Select a different disk without changing the default:

from orionis.http import JSONResponse
from orionis.support.facades import Storage
class ReportController:
async def store(self) -> JSONResponse:
disk = Storage.disk("local")
report = await disk.put("reports/latest.csv", "id,total\n1,25\n")
return JSONResponse({"disk": disk.name(), "path": report.path()})

Explicit selection uses the name as received; use "s3", rather than "S3" or "aws". An unconfigured name raises DiskNotFoundException. disk(None) and disk("") use the default. disk() and default() are synchronous; await the disk or file’s I/O operations.

The public module exports these entities:

from orionis.foundation.config.filesystems import (
GCS, S3, Azure, DiskName, Disks, Filesystems, Local, Public,
)
Disks fieldAccepted valueDefault driver
localLocal | dictlocal
publicPublic | dictlocal
s3S3 | dictaws
azureAzure | dictazure
gcsGCS | dictgcs

All constructor fields are optional; omitted entries use their factories. Disks(local={...}) converts that dictionary to Local(...), and the same applies to the other entries. Other object types, including None, raise TypeError. Instances do not share the entities created by their factories.

The standard configuration has five fixed entries. Adding an archive dictionary key does not register a disk and raises TypeError. Filesystems(default="archive") is also rejected. extend() expands the driver registry rather than the disk catalog. Change an existing entry’s driver to use another implementation; a wider configuration structure requires consistent extensions to its entities and validation.

FieldVariable and defaultType and effect
driverNo variable; "local".Nonempty str. Selects the implementation.
pathLOCAL_PATH; "storage/app/private".Nonempty str. Absolute root or path relative to the application.

Local has no url field. Changing driver to memory still validates path, although that driver does not use the path.

FieldVariable and defaultType and effect
driverNo variable; "local".Nonempty str. Selects the implementation.
pathPUBLIC_PATH; "storage/app/public".Nonempty str. Public disk root.
urlPUBLIC_URL; "/static".Nonempty str. Base used by file.url().

Public.url remains required by its entity when selecting another driver. Configuration validation does not create directories. The local driver creates its root when constructed and required parents when writing. A URL does not register a static route: the application or server must publish the directory.

FieldVariable and defaultType and effect
driverNo variable; "aws".Nonempty str; built-in aliases aws and s3.
keyS3_KEY; "".str. Access identifier, used with secret.
secretS3_SECRET; "".str. Access secret, used with key.
regionS3_REGION; "us-east-1".Nonempty str. Client and derived URL region.
bucketS3_BUCKET; "".str. Object bucket.
urlS3_URL; None.str | None. Public base or CDN used to generate URLs.
endpointS3_ENDPOINT; None.str | None. S3 or compatible service API endpoint.
use_path_style_endpointS3_USE_PATH_STYLE_ENDPOINT; False.bool. Enables boto3 addressing_style="path".

The entity accepts empty credentials and bucket strings to allow initial configuration. This does not demonstrate a usable service. If key and secret are not both present, boto3 resolves its own credentials. url and endpoint are different: a CDN base does not change the API client.

FieldVariable and defaultType and effect
driverNo variable; "azure".Nonempty str. Selects the implementation.
connection_stringAZURE_CONNECTION_STRING; "".str. Takes precedence when creating the client if present.
account_nameAZURE_ACCOUNT_NAME; "".str. Account name, also extracted from the connection string.
account_keyAZURE_ACCOUNT_KEY; "".str. Account key, also extracted from the connection string; required for SAS.
containerAZURE_CONTAINER; "".str. Blob container.
urlAZURE_URL; None.str | None. Public base overriding the derived address.

The entity validates types without requiring a complete connection string, account, or container. The client and service detect connection or authorization errors on use. url does not override the Azure connection endpoint.

FieldVariable and defaultType and effect
driverNo variable; "gcs".Nonempty str; built-in aliases gcs and google.
project_idGCS_PROJECT_ID; "".str. Project for the constructor without a key file.
key_fileGCS_KEY_FILE; None.str | None. Service-account JSON file, passed directly to the SDK.
bucketGCS_BUCKET; "".str. Object bucket.
urlGCS_URL; None.str | None. Public base overriding storage.googleapis.com.

An empty or None key_file selects Application Default Credentials. A nonempty path selects Client.from_service_account_json(...); its existence and contents are checked when creating the SDK client, rather than when constructing the entity.

Keep factories using Env.get(...) to resolve environment values. You can specify only changed entries and leave the rest at their defaults. This complete file customizes the two local roots:

from dataclasses import dataclass, field
from orionis.environment import Env
from orionis.foundation.config.filesystems import (
DiskName, Disks, Filesystems, Local, Public,
)
@dataclass(frozen=True, kw_only=True)
class BootstrapFilesystems(Filesystems):
default: DiskName | str = field(
default_factory=lambda: Env.get("FILESYSTEM_DISK", DiskName.LOCAL),
)
disks: Disks | dict = field(
default_factory=lambda: Disks(
local=Local(path=Env.get("LOCAL_PATH", "storage/app/private")),
public=Public(
path=Env.get("PUBLIC_PATH", "storage/app/public"),
url=Env.get("PUBLIC_URL", "/static"),
),
),
)

Omitted entities such as S3 or Azure retain their environment factories. To customize them, add their documented fields to Disks(...). Change driver fields in Python: the included file defines no environment variables for them.

Equivalent dictionaries are supported. The manager converts the root to Filesystems; that entity converts disks and its entries to their corresponding entities:

from orionis.foundation.config.filesystems import Filesystems
settings = Filesystems(
default="public",
disks={
"local": {"driver": "local", "path": "storage/app/private"},
"public": {
"driver": "local",
"path": "storage/app/public",
"url": "/static",
},
},
)

Dictionaries retain validation and do not accept arbitrary keys. Existing entities are retained; dictionaries are reconstructed using the real constructors. In Python, use a boolean for use_path_style_endpoint: True, rather than "true". In .env, Env.get(...) reads the value before entity validation.

Validation checks the types listed above and rejects required strings that are empty or contain only whitespace. Optional cloud fields accept None where indicated; drivers treat their empty strings as absent. Validation does not check URL formats, buckets, credentials, or connectivity.

A nonempty driver can be an extension, so the entity accepts a name not yet registered. If no built-in implementation or factory resolves it, disk selection raises DriverNotSupportedException. Register its factory with Storage.extend(...).

The manager retains its resolved configuration and default disk. Changing an environment variable after obtaining the service does not rebuild disks; configure them before startup. Entities are immutable. extend() invalidates the disk cache so later resolutions use the registered factory, but does not reread the environment.

Continue with drivers and dependencies for backend authentication and differences in publishing and temporary URLs.