Orientation
File Storage (modules/FileStorage/, slug file_storage, module setting module_file_storage) is a logical layer over ERPat's shared general_files registry. It manages only its own logical files (file_nodes) and never surfaces or mutates files owned by other modules. Physical bytes reuse ERPat's local / AWS S3 pipeline (private objects, signed URLs).
Everything is multi-tenant: all rows live in the tenant database with no tenant_id column — isolation comes from the per-tenant connection. New rows end with the standard audit block (created_by, created_at, updated_at, deleted) and are soft-deleted.
app: file_storageadmin: file_storage_adminpublic: f/* client API: /api/v1/file-storageend-user API: /v1/api/file-storage
Data model — tables
Thirteen tenant-database tables. Every table filters deleted = 0 and carries the standard audit columns.
| Table | Purpose (one line) |
|---|---|
file_spaces | Team Spaces — tenant-owned shared containers (department-synced membership, min. two managers). |
file_nodes | The logical tree — folders and files owned by File Storage (the module's own view over storage). |
file_versions | Immutable version history per file node; each version points at a general_files row. |
general_file_references | Reference ledger linking logical files/versions to physical general_files bytes (drives reference-aware purge). |
file_acl_entries | Role-based access grants on nodes/spaces (viewer → owner); inherited and computed server-side. |
file_upload_sessions | In-flight upload intake — validated staging consumed by the upload-pipeline cron. |
file_activity_events | Activity feed rows (per-node/space event stream shown in the Activity tab). |
file_favorites | Per-user starred nodes (the Starred tab). |
file_comments | Threaded comments on file nodes (R2 collaboration). |
file_share_links | External share links — stores only the SHA-256 token hash, scope, expiry, max-opens, Argon2id password, OTP settings. |
file_legal_holds | Legal-hold definitions that block permanent purge (fail-closed governance). |
file_legal_hold_items | The specific nodes/versions bound to a legal hold. |
general_files rows the module creates are owner-less to the rest of ERPat, so other modules' listings never see File Storage bytes and vice-versa.Screens (tabs)
Workspace — FileStorage (one page, lazy tabs, never reloads)
| Tab | Shows |
|---|---|
| My Files | The signed-in user's personal folder tree and files. |
| Team Spaces | Tenant-owned shared spaces the user can access. |
| Shared with Me | Files/folders granted to the user directly, by group, department, or tenant-wide. |
| Recent | Recently touched files. |
| Starred | Files the user favorited (file_favorites). |
| Trash | Soft-deleted items, recoverable via restore. |
| Activity | The user-scoped activity feed. |
Governance console — FileStorage_admin (separate admin page)
| Tab | Shows |
|---|---|
| Policies | Purpose-policy registry and storage policy settings. |
| Quarantine | Upload quarantine review queue. |
| Legal Holds | Create and manage legal holds that block purge. |
| Risk | Risk dashboard (populated by the risk-scan cron). |
Controllers & routes
App workspace — FileStorage
Authenticated staff controller. Base URL prefix file_storage. Gated by module_file_storage and the file_storage permission.
| Route prefix | Purpose |
|---|---|
file_storage | Workspace shell (lazy tabs listed above). |
Governance console — FileStorage_admin
High-risk admin controller for the governance console. Base URL prefix file_storage_admin. Gated by the governance permission family.
| Route prefix | Purpose |
|---|---|
file_storage_admin | Governance console (Policies, Quarantine, Legal Holds, Risk). |
Public share pages — FileShare (GuestController)
Unauthenticated share-link surface. Only the SHA-256 token hash is stored server-side; scopes, expiry, atomic max-opens, Argon2id password, and email OTP are enforced here.
| Route | Purpose |
|---|---|
f/s | Share-link landing (resolve token → gate on password/OTP/expiry/opens). |
f/d | Download the shared file (only when the link scope permits downloads). |
f/verify | Verify the link password gate. |
f/otp | Email one-time-passcode gate for the link. |
f/ru | Upload-only file request — recipient submits files (upload_only scope). |
f/request | File-request landing (collect files through an upload-only link). |
Integration (client) API — FileStorage_api
Machine-to-machine API. Base path /api/v1/file-storage. Every action calls authorizeScope() as its first line; scopes are filestorage:read and filestorage:write.
| Base path | Scopes | Purpose |
|---|---|---|
/api/v1/file-storage/* | filestorage:read, filestorage:write | Integration access to logical files and folders (read/write gated per action). |
End-user API — FileStorage_euapi
Self-service API for a signed-in end user. Base path /v1/api/file-storage. Every action runs requireModule('module_file_storage') + requirePermit(...) and resolves identity only through selfUserId().
| Endpoint | Purpose |
|---|---|
/v1/api/file-storage/list | List the caller's own files/folders. |
/v1/api/file-storage/view | View metadata for a file the caller owns/can access. |
/v1/api/file-storage/download | Download a file the caller can access. |
/v1/api/file-storage/recent | The caller's recent files. |
/v1/api/file-storage/starred | The caller's starred files. |
selfUserId() — the end-user API cannot act on another user's files.Models
Models extend Crud_model and hold all SQL. They map to the tables above (e.g. spaces, nodes, versions, references, ACL entries, upload sessions, activity events, favorites, comments, share links, legal holds). All queries filter deleted = 0 and are tenant-DB scoped.
Service libraries (libraries/)
Business logic lives in services so controllers stay thin. Each is a CI-loadable class.
| Service | Responsibility |
|---|---|
File_registry_service | Bridges logical files to the shared general_files registry and the reference ledger. |
File_node_service | Folder/file tree operations — create, rename, move, trash, restore, star. |
File_upload_service | Validated intake — reuses ERPat's extension/MIME/magic-byte checks and stages upload sessions. |
File_lifecycle_service | Versioning, trash/restore, and reference-aware safe purge (fail-closed). |
File_authorization_service | Computes effective ACLs server-side with folder + space inheritance. |
File_share_service | Creates/validates share links — token hashing, scope, expiry, max-opens, password, OTP. |
File_scanner_service | Pluggable scanner hook (quarantine/CDR/AV integration point — no engine enabled today). |
File_purpose_policy | Purpose-policy registry — resolves storage policy for a file's declared purpose. |
File_scanner_service is a hook only: real AV / content-disarm scanning is a future integration and is not active. Upload safety today rests on ERPat's existing validation pipeline.Cron jobs
Advanced Cron jobs discovered from modules/FileStorage/jobs/. Each runs per-tenant and self-gates on module_file_storage.
| Slug | Schedule | Purpose |
|---|---|---|
file_storage_upload_pipeline | every 1 min (*/1) | Processes validated upload sessions into logical files/versions. |
file_storage_access_expirer | every 5 min (*/5) | Expires share links / access grants past their expiry. |
file_storage_membership_sync | daily | Syncs Team Space membership from department membership. |
file_storage_registry_reconcile | hourly | Reconciles the reference ledger against the general_files registry. |
file_storage_safe_purge | daily | Reference-aware permanent purge (blocked by active reference, current version, retention, or legal hold). |
file_storage_risk_scan | daily | Populates the Governance risk dashboard. |
Dashboard widgets
| Widget | Purpose |
|---|---|
| My Recent Files | Quick access to the user's most recently touched files. |
| Storage Usage | At-a-glance storage consumption for the user/tenant. |
module_file_storage + the file_storage permission.Config sidecars (modules/FileStorage/config/)
| File | Purpose |
|---|---|
module_config.php | Manage-Modules registration (module_key = file_storage). |
routes.php | All module routes — app, admin, and the public f/* share pages. |
menu.php | Left-menu slice under the "Storage" group. |
default_menu.php | Default-left-menu slice for the customization editor. |
permissions.php | RBAC keys for the Roles editor (see below). |
widgets.php | Dashboard widget registry (My Recent Files, Storage Usage). |
api_routes.php / euapi_routes.php | Client API (/api/v1/file-storage) and end-user API (/v1/api/file-storage) route DSL. |
api_scope.php | OAuth scope catalog: filestorage:read, filestorage:write. |
system_logs.php | Audit event-key registration. |
Permission keys
Base CRUD plus sharing/space/request grants, plus a high-risk governance family.
| Key | Grants |
|---|---|
file_storage | Base access to the module. |
file_storage_create | Create folders/files. |
file_storage_update | Rename/move/version files. |
file_storage_delete | Trash/restore/delete files. |
file_storage_share_internal | Share files with other ERPat users/groups. |
file_storage_share_external | Create external share links. |
file_storage_manage_permissions | Manage ACL grants on nodes/spaces. |
file_storage_create_space | Create Team Spaces. |
file_storage_manage_space | Manage Team Space settings/membership. |
file_storage_create_file_request | Create upload-only file requests. |
Governance (high-risk)
| Key | Grants |
|---|---|
governance_view_activity | View the governance activity/audit views. |
governance_manage_quarantine | Review and release/reject quarantined uploads. |
governance_manage_policies | Edit purpose/storage policies. |
governance_legal_hold | Create and manage legal holds. |
governance_purge | Perform reference-aware permanent purge. |
governance_* keys gate destructive and compliance-critical actions — grant them only to compliance/admin roles.ACL roles
Role-based ACL entries on nodes/spaces, computed server-side with folder + space inheritance. From least to most privileged:
viewercommentercontributor editormanagerowner upload_only
upload_only is a special role for file requests — it permits contributing files without read access to the container.
Share-link scopes
| Scope | Meaning |
|---|---|
restricted | Only explicitly named recipients. |
tenant | Anyone in the tenant. |
external_recipients | Specified external email recipients. |
anyone | Anyone with the link. |
upload_only | File request — recipients upload, cannot browse/download. |
Audit — system_logs event keys
Registered in config/system_logs.php. File Storage writes an audit entry for every mutation; events are keyed <field_name>:<module_name> under the module's namespace.
| Event area | Logged when |
|---|---|
| File / folder lifecycle | Create, rename, move, trash, restore, new version. |
| Sharing | Internal share granted/revoked; external link created/opened/expired. |
| Permissions | ACL entry added/changed/removed. |
| Team Spaces | Space created; membership synced/changed. |
| File requests | Upload-only request created; file submitted. |
| Governance | Policy change, quarantine review, legal hold placed/lifted, purge performed. |
file_activity_events; the compliance audit trail is the standard ERPat system_logs. They are complementary — one is user-facing, one is immutable audit.Setup quick reference
1. Enable the module (Settings → Manage Modules → File Storage) 2. php erpat migrate:modules (creates the 13 tenant tables) 3. Assign permissions (Roles editor: file_storage_*, governance_*) 4. (optional) Set policies (Governance → Policies) 5. (optional) php erpat db:seed FileStorageDemo