Architecture Overview
What is FlatImage?
FlatImage is a single-file container system for Linux. Everythingβcode, dependencies, configuration, and filesystemsβis packaged into one executable. No installation required, no external files needed.
Key Features:
- π¦ Single file - One executable contains everything
- π Sandboxed - Secure isolation with granular permissions
- βοΈ Reconfigurable - Change settings after build without recompiling
- ποΈ Compressed - DwarFS compression for efficient storage
- π Integrated - Desktop integration, portal IPC, Wine/Proton support
Architecture Modules
FlatImage is organized into distinct modules, each handling a specific responsibility:
1. Boot Module
The boot module is the entry point for FlatImage execution. It handles initial setup and binary relocation.
FlatImage] --> B{In /tmp?} B -->|No| C[Copy to /tmp
Re-execute] B -->|Yes| D[Set metadata
env vars] C --> D D --> E[Verify FUSE
module] E --> F[Initialize
configuration] F --> G[Continue to
main boot] style A fill:#90EE90 style G fill:#87CEEB
Responsibilities:
- Initialize logging system
- Set version/commit/distribution metadata
- Relocate binary to
/tmpif needed (FUSE requirement) - Verify FUSE kernel module is loaded
- Calculate filesystem offsets in binary
2. Config Module
The config module is the central configuration object that contains all runtime settings. Every other module depends on this.
/tmp/fim/...] Path --> Files[Files
bashrc, passwd] Path --> Bins[Binaries
bash, portal, janitor] Logs --> BootLog[boot.log] Logs --> FuseLogs[fuse/*.log] Logs --> PortalLogs[daemon/*.log] Flags --> Casefold[Casefold
on/off] Flags --> Overlay[Overlay Type
unionfs/overlayfs/bwrap] ModuleConfig --> FSConfig[Filesystem
Controller Config] ModuleConfig --> PortalConfig[Portal
Daemon Config] style Config fill:#FFD700
Responsibilities:
- Define all filesystem paths (
FIM_DIR_*) - Configure logging for all subsystems
- Read flags from reserved space (casefold, overlay type)
- Set up module configurations
- Extract embedded binaries to
/tmp/fim/.../bin/ - Set environment variables
3. Reserved Space Module
Reserved space is a configuration storage area embedded in the ELF binary. This enables post-build reconfiguration.
+ Code] ELF --> Reserved[Reserved Space
~4 MB] Reserved --> DwarFS[DwarFS
Layers] Reserved --> Perms[Permissions] Reserved --> Boot[Boot Command] Reserved --> Env[Environment] Reserved --> Bind[Bind Mounts] Reserved --> Desktop[Desktop Config] Reserved --> Icon[Icon] Reserved --> Other[Notify, Overlay,Casefold, Remote] style Reserved fill:#FFA500
Responsibilities:
- Store configuration data in the binary
- Provide read/write functions for configuration
- Validate space constraints at compile-time
- Enable
fim-*commands to modify binary
Configuration Components:
- Permissions - Permission bitfield
- Boot Command - Default command
- Environment - Custom env vars
- Bind Mounts - Host-guest mounts
- Icon - PNG icon data
- Desktop - Desktop integration
- Notify, Overlay, Casefold - Flags
4. DB Module
The DB module provides safe wrappers around reserved space for complex configurations stored as JSON.
recipes/] style DB fill:#87CEEB
Responsibilities:
- Serialize/deserialize configuration to JSON
- Provide high-level APIs for configuration management
- Abstract away reserved space offsets
- Validate configuration data
5. Filesystems Module
The filesystems module mounts and manages the layered filesystem stack that provides the container's root.
optional] Controller --> Spawn[Spawn Janitor] Mount1 --> Layer0[Layer 0
Base] Mount1 --> Layer1[Layer 1
Committed] Mount1 --> LayerN[Layer N
External] Mount2 --> Union{Overlay Type?} Union -->|unionfs| UnionFS[UnionFS-FUSE] Union -->|overlayfs| OverlayFS[FUSE-OverlayFS] Union -->|bwrap| Native[Native Overlay] Mount3 --> CIOPFS[Case-Insensitive
Layer] Layer0 --> Merged[Merged Root
Filesystem] Layer1 --> Merged LayerN --> Merged UnionFS --> Merged OverlayFS --> Merged Native --> Merged CIOPFS --> Merged style Controller fill:#FFD700 style Merged fill:#90EE90
Responsibilities:
- Mount DwarFS compressed layers
- Set up overlay filesystem (writable layer)
- Optionally mount CIOPFS for case-insensitivity
- Coordinate filesystem lifecycle
- Spawn janitor for cleanup
Layer Stack:
βββββββββββββββββββββββ
β CIOPFS (optional) β β Case-insensitive
βββββββββββββββββββββββ€
β Overlay (writable) β β Changes go here
βββββββββββββββββββββββ€
β Layer N β β External/committed
β Layer 1 β β Committed
β Layer 0 β β Base
βββββββββββββββββββββββ
6. Janitor Module
The janitor is a cleanup daemon that ensures filesystems are properly unmounted if the parent process crashes.
Responsibilities:
- Monitor parent process health
- Unmount filesystems on parent death
- Prevent stale FUSE mounts
- Log cleanup operations
Why It's Needed:
- If FlatImage crashes, FUSE mounts remain
- Stale mounts can cause system issues
- Janitor ensures clean cleanup
7. Portal Module
The portal module provides IPC between host and container using FIFO-based message passing.
for JSON requests"] HostFork["Fork child process"] HostExec["Execute on host
with I/O via FIFOs"] HostLoop -->|Request received| HostFork HostFork --> HostExec HostExec -.Return to loop.-> HostLoop end subgraph GuestDaemon["Portal Daemon (Guest Mode)
Optional"] GuestLoop["Loop: Poll daemon.guest.fifo
for JSON requests"] GuestFork["Fork child process"] GuestExec["Execute in container
with I/O via FIFOs"] GuestLoop -->|Request received| GuestFork GuestFork --> GuestExec GuestExec -.Return to loop.-> GuestLoop end FimPortal["fim_portal
Inside Container"] FimInstance["fim-instance exec
On Host"] FimPortal -->|Send JSON request| HostLoop HostExec -->|stdin/stdout/stderr
via FIFOs| FimPortal FimInstance -->|Send JSON request| GuestLoop GuestExec -->|stdin/stdout/stderr
via FIFOs| FimInstance style HostDaemon fill:#E8F5E9 style GuestDaemon fill:#E3F2FD style FimPortal fill:#FFF3E0 style FimInstance fill:#FFF3E0
Responsibilities:
- Enable host-container command execution
- Redirect stdin/stdout/stderr between processes
- Forward signals (Ctrl+C, etc.)
- Maintain FIFO communication channels
How It Works:
- Daemon listens on FIFO for requests
- Dispatcher sends JSON request to daemon
- Daemon forks child process to execute command
- Child redirects I/O through FIFOs
- Dispatcher forwards I/O to/from caller
- Exit code returned via FIFO
Use Cases:
- Execute host commands from container
- Execute container commands from host
- Multi-instance communication
8. Subprocess Module
The subprocess module provides process management utilities for spawning and controlling child processes.
Handle] Child --> PID[get_pid] Child --> Wait[wait] Child --> Kill[kill] Child --> Running[is_running] style Subprocess fill:#87CEEB style Child fill:#90EE90
Responsibilities:
- Build process execution configuration
- Fork and exec child processes
- Manage process lifetime
- Provide process control (kill, wait, status)
Key Features:
- Builder pattern for configuration
- RAII process management
- Signal handling
- Log file redirection
Used By:
- Portal daemon (spawning commands)
- Filesystem controller (spawning janitor)
- FUSE mounts (spawning dwarfs, overlayfs, etc.)
9. Bwrap Module
The bwrap module is a wrapper around Bubblewrap that creates the sandboxed container environment.
HOME, GPU, NETWORK, etc.] User[User Data
uid/gid, name, home, shell] Binds[Bind Mounts
RO/RW/DEV] Env[Environment Variables] Overlay[Overlay Config
layers, upper, work] end subgraph BwrapClass["Bwrap Class"] direction TB BuildArgs["Build Arguments
--bind, --dev-bind, --ro-bind
--setenv, --uid, --gid
--tmpfs, --proc, --dev"] ApplyPerms["Apply Permissions
Loop through bitfield"] PermChecks["Permission Mappings:
HOME β --bind $HOME $HOME
MEDIA β --bind /media /media
AUDIO β --ro-bind /dev/snd
WAYLAND β --ro-bind $XDG_RUNTIME_DIR
XORG β --ro-bind /tmp/.X11-unix
GPU β --dev-bind /dev/dri + NVIDIA symlinks
NETWORK β --share-net
DBUS_USER β --bind dbus user socket
DBUS_SYSTEM β --bind dbus system socket
UDEV β --ro-bind /run/udev
INPUT β --dev-bind /dev/input
USB β --dev-bind /dev/bus/usb
SHM β --bind /dev/shm
OPTICAL β --ro-bind /dev/sr*
DEV β --dev /dev"] BuildArgs --> ApplyPerms ApplyPerms --> PermChecks end subgraph Execution["Execute Container"] SetupOverlay["Setup Filesystem:
Native overlay OR
--bind mount/ /"] SpawnPortal["Spawn Portal Daemon:
nohup daemon & disown"] ExecCommand["Execute Command:
bash -c 'program args'"] ErrorPipe["Error Pipe:
syscall_nr, errno_nr"] SetupOverlay --> SpawnPortal SpawnPortal --> ExecCommand ExecCommand --> ErrorPipe end Perms --> ApplyPerms User --> BuildArgs Binds --> BuildArgs Env --> BuildArgs Overlay --> SetupOverlay PermChecks --> SetupOverlay style BwrapClass fill:#FFE4B5 style Input fill:#E3F2FD style Execution fill:#E8F5E9
Responsibilities:
- Construct bubblewrap command line
- Apply permission-based filesystem binds
- Configure container namespaces
- Set user/group identity
- Apply environment variables
- Execute command in sandbox
Isolation:
- Default: Full isolation, no host access
- Opt-in: Each permission grants specific access
- Namespaces: Separate process, mount, IPC, network spaces
Module Interaction Flow
Here's how the modules work together during a typical FlatImage execution:
Key Architectural Patterns
1. Central Configuration Object
The FlatImage struct in config.hpp acts as a single source of truth for all runtime configuration. Every module receives its configuration from this central object.
Benefits:
- Predictable initialization order
- No global state scattered across modules
- Easy to understand dependencies
2. Reserved Space for Persistence
Configuration is stored inside the binary rather than external files. This enables single-file portability while maintaining configurability.
Benefits:
- No external configuration files
- Configuration travels with binary
- Post-build reconfiguration
3. Layered Filesystem
Multiple read-only compressed layers with a writable overlay on top provides efficiency and flexibility.
Benefits:
- High compression ratios
- Incremental updates via layer commits
- Shared base layers
- Fast decompression
4. Process Isolation via Namespaces
Uses Linux user namespaces and Bubblewrap for unprivileged sandboxing with granular permissions.
Benefits:
- No root required
- Fine-grained access control
- Standard Linux isolation
5. FIFO-based IPC
The portal system uses named pipes (FIFOs) for simple, reliable host-container communication.
Benefits:
- Standard Unix IPC
- Full I/O redirection
- Signal forwarding
- No complex protocols