4.3 KiB
Architecture
Iron is an async Python modular monolith with a React single-page Web app.
System Overview
Browser UI
|
FastAPI app
|
Services
|
Repositories
|
SQLite metadata database
|
Storage adapters: local directory, S3, WebDAV
Backend
Core stack:
- FastAPI
- SQLAlchemy 2.x async ORM
- SQLite via
aiosqlite - Alembic migrations
- durable in-process jobs stored in SQLite
Important packages:
app/api/: route handlers and dependency wiringapp/services/: business logicapp/repositories/: database accessapp/models/: SQLAlchemy entitiesapp/schemas/: API schemasapp/adapters/storage/: storage adapter protocol, registry, and backend implementations
Frontend
Core stack:
- Vite
- React
- TypeScript
- React Router
- TanStack Query
Source lives in frontend/. Built assets are emitted into app/web/dist/ and
served by FastAPI at /app.
Data Model
Main tables:
usersauth_sessionsdirectoriesfile_entriesfile_versionsblobsblob_replicasbackendsupload_sessionsupload_session_partsjobsplacement_policiespreview_artifactscache_entries
Design principles:
- logical namespace is separate from physical storage placement
- files have immutable content versions
- physical content is represented as blobs and replicas
- user-facing deletion uses recycle-bin semantics
- jobs are durable and retryable
- backend-specific configuration is validated before persistence and stays behind adapter boundaries
Upload And Read Path
Uploads use tus transport under /files.
Flow:
- create upload session
- append bytes with
PATCH - finalize through the API
- create file metadata, version, blob, and local replica
- enqueue replication or preview jobs when needed
Read path:
- resolve file and current version
- find the best local or cached blob
- if needed, materialize a ready remote replica into cache
- serve download, preview, or range stream
Browser File Access
JSON APIs use bearer-token authorization through the shared frontend API client.
Browser preview and download must not use naked /api/files/... media URLs in
img, iframe, video, or window.open. Fetch file content with
authorization, create an object URL, use it, then revoke it.
This is covered by the Playwright E2E flow.
Jobs
Jobs are stored in SQLite and executed by the in-process job loop.
Current job types include:
- blob replication
- preview artifact generation
- backend health checks
- full reconcile
- file reconcile
Every job should be safe to retry.
Storage Backends
Supported backend types:
local_directory: a filesystem directory used for default local persistence and low-latency readss3: S3 protocol storage, including AWS S3, MinIO, and compatible object storeswebdav: WebDAV storage, including OpenList-managed cloud drive directories exposed through a dedicatedroot_path
Replica object keys are written into a structured application-owned layout such
as objects/file/sha256/73/35/<hash>.blob. This keeps remote storage organized
under a predictable object tree without trying to disguise replica content as
user-authored media files. During the current development stage, Iron treats
this layout as the only supported remote object format and does not preserve
compatibility with earlier experimental paths.
Reserved object-tree namespaces:
objects/file/...: primary file blobs and replica payloadsobjects/preview/<artifact_type>/...: generated previews such asinline_preview,thumbnail, andposterobjects/export/metadata/YYYY/MM/DD/...: metadata snapshot exportsobjects/manifest/<scope>/...: future manifest-style control files such as placement or repair manifests
Backend config is normalized through typed schemas before it is stored. Public
config and encrypted secret config are persisted separately. IRON_SECRET_KEY
derives the encryption key for stored backend credentials. API responses include
public config plus a list of configured secret fields so operators can identify
a backend without exposing access keys, client secrets, or refresh tokens. Update
requests can change public config and provide secret overrides; omitted secret
fields keep their previous encrypted values. Disabled backends can be deleted
only when they are no longer referenced by policies and no blob replicas remain
stored on them.