Documentation

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    SubscriptionConductor
    PaymentConductor
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    Development
    Ring MCP Server

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)

    Documentation

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    SubscriptionConductor
    PaymentConductor
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    Development
    Ring MCP Server

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)

    Documentation

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    SubscriptionConductor
    PaymentConductor
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    Development
    Ring MCP Server

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)
    1. Docs
    2. /Features
    3. /Ring File Cabinet

    Updated Jul 22, 20267 min listen

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    1. Docs
    2. /Features
    3. /Ring File Cabinet

    Updated Jul 22, 20267 min listen

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    1. Docs
    2. /Features
    3. /Ring File Cabinet

    Updated Jul 22, 20267 min listen

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    Ring File Cabinet

    Use Founder / Developer tabs in the docs sidebar to filter this page. Uploads ride RingFileBase; public gallery bytes ride Ring CDN /files/{uuid}.

    Ring File Cabinet is the personal file manager for Ring clones: nested folders (max depth 3), a three-column desktop workspace, owner / trustee ACL (trustee = view + download only), a shared-with-me desktop for every subscriber+, private gallery curation, and a public /{username}/img lightbox for items marked visibility=public.

    Access gates (verified)

    SurfaceWhoBehavior
    /file-cabinetAuth; member+Own manager — FileCabinetWrapper → FileCabinetDesktop. Non-members see MemberUpgradeGate (same pattern as add-entity).
    /profile/cabinet—Redirects to /file-cabinet (ROUTES.PROFILE_CABINET aliases FILE_CABINET).
    /profile/sharedsubscriber+Full drag desktop of trustee shares. Subscriber-only users also see upgrade CTA membersUpgrade → membership with returnTo=/profile/shared.
    /profile/galleryAuth; member+Curate image/video → private | unlisted | public (MemberUpgradeGate for non-members).
    /{username}/imgPublicLightbox for visibility=public items.
    /{username}/playerPublicMood player (renamed from /songs; no legacy redirect). Private manage stays /profile/songs.

    ACL grants are immediate (no invite-accept). Max upload 25MB via getCabinetStorageConfig(). Publications use a separate co-author model — not File Cabinet ACL.

    Member-gated own manager · shared for all subscribers

    Own File Cabinet (/file-cabinet) and gallery manage (/profile/gallery) require member privileges (hasMemberPrivileges + MemberUpgradeGate). All subscribers (and above) open /profile/shared for trustee shares — they do not get an own desktop or upload surface until they upgrade to member.

    Why this matters for your clone

    Members need a place for docs, scans, and media that is not chat attachment chaos and not the Admin Wiki vault. File Cabinet is personal storage with trusted viewers, a shared inbox for collaborators, and a public gallery face — useful for marketplaces, service rings, and any clone where trust includes “show my work.”

    Who gets what (operator view)

    File Cabinet access gates

    Own file manager (member+)

    Members open /file-cabinet — upload, folders, trustees via ContactPicker, expandable tree + options rail. Non-members hit the membership upgrade gate.

    Shared for all subscribers

    Every subscriber+ uses /profile/shared for trustee shares (view/download desktop). An in-rail upgrade CTA points to membership for an own manager.

    Architecture

    Role gates in code (verified)

    SurfaceGate
    app/.../file-cabinet/page.tsxauth → hasMemberPrivileges else MemberUpgradeGate (returnTo = ROUTES.FILE_CABINET)
    app/.../profile/shared/page.tsxauth → hasRoleAtLeast(..., subscriber) else unauthorized
    app/.../profile/gallery/page.tsxauth → hasMemberPrivileges else MemberUpgradeGate
    app/.../profile/cabinet/page.tsxRedirect → ROUTES.FILE_CABINET

    Related documentation

    Related documentation

    RingFileBase (object storage API)

    Depends-on: cabinet uploads go through file() / RingBaseAdapter and getCabinetStorageConfig.

    Ring CDN (RingFileBase edge)

    Depends-on: public gallery items use stable CDN /files/{uuid} (+ _v_* derivatives).

    Public Profile Pages

    Next-step: public /img, /player, and /games surfaces hang off /{username}.

    Peer Games

    See-also: public /games availability is a sibling profile surface (not File Cabinet ACL).

    Ring File Cabinet

    Use Founder / Developer tabs in the docs sidebar to filter this page. Uploads ride RingFileBase; public gallery bytes ride Ring CDN /files/{uuid}.

    Ring File Cabinet is the personal file manager for Ring clones: nested folders (max depth 3), a three-column desktop workspace, owner / trustee ACL (trustee = view + download only), a shared-with-me desktop for every subscriber+, private gallery curation, and a public /{username}/img lightbox for items marked visibility=public.

    Access gates (verified)

    SurfaceWhoBehavior
    /file-cabinetAuth; member+Own manager — FileCabinetWrapper → FileCabinetDesktop. Non-members see MemberUpgradeGate (same pattern as add-entity).
    /profile/cabinet—Redirects to /file-cabinet (ROUTES.PROFILE_CABINET aliases FILE_CABINET).
    /profile/sharedsubscriber+Full drag desktop of trustee shares. Subscriber-only users also see upgrade CTA membersUpgrade → membership with returnTo=/profile/shared.
    /profile/galleryAuth; member+Curate image/video → private | unlisted | public (MemberUpgradeGate for non-members).
    /{username}/imgPublicLightbox for visibility=public items.
    /{username}/playerPublicMood player (renamed from /songs; no legacy redirect). Private manage stays /profile/songs.

    ACL grants are immediate (no invite-accept). Max upload 25MB via getCabinetStorageConfig(). Publications use a separate co-author model — not File Cabinet ACL.

    Member-gated own manager · shared for all subscribers

    Own File Cabinet (/file-cabinet) and gallery manage (/profile/gallery) require member privileges (hasMemberPrivileges + MemberUpgradeGate). All subscribers (and above) open /profile/shared for trustee shares — they do not get an own desktop or upload surface until they upgrade to member.

    Why this matters for your clone

    Members need a place for docs, scans, and media that is not chat attachment chaos and not the Admin Wiki vault. File Cabinet is personal storage with trusted viewers, a shared inbox for collaborators, and a public gallery face — useful for marketplaces, service rings, and any clone where trust includes “show my work.”

    Who gets what (operator view)

    File Cabinet access gates

    Own file manager (member+)

    Members open /file-cabinet — upload, folders, trustees via ContactPicker, expandable tree + options rail. Non-members hit the membership upgrade gate.

    Shared for all subscribers

    Every subscriber+ uses /profile/shared for trustee shares (view/download desktop). An in-rail upgrade CTA points to membership for an own manager.

    Architecture

    Role gates in code (verified)

    SurfaceGate
    app/.../file-cabinet/page.tsxauth → hasMemberPrivileges else MemberUpgradeGate (returnTo = ROUTES.FILE_CABINET)
    app/.../profile/shared/page.tsxauth → hasRoleAtLeast(..., subscriber) else unauthorized
    app/.../profile/gallery/page.tsxauth → hasMemberPrivileges else MemberUpgradeGate
    app/.../profile/cabinet/page.tsxRedirect → ROUTES.FILE_CABINET

    Related documentation

    Related documentation

    RingFileBase (object storage API)

    Depends-on: cabinet uploads go through file() / RingBaseAdapter and getCabinetStorageConfig.

    Ring CDN (RingFileBase edge)

    Depends-on: public gallery items use stable CDN /files/{uuid} (+ _v_* derivatives).

    Public Profile Pages

    Next-step: public /img, /player, and /games surfaces hang off /{username}.

    Peer Games

    See-also: public /games availability is a sibling profile surface (not File Cabinet ACL).

    Ring File Cabinet

    Use Founder / Developer tabs in the docs sidebar to filter this page. Uploads ride RingFileBase; public gallery bytes ride Ring CDN /files/{uuid}.

    Ring File Cabinet is the personal file manager for Ring clones: nested folders (max depth 3), a three-column desktop workspace, owner / trustee ACL (trustee = view + download only), a shared-with-me desktop for every subscriber+, private gallery curation, and a public /{username}/img lightbox for items marked visibility=public.

    Access gates (verified)

    SurfaceWhoBehavior
    /file-cabinetAuth; member+Own manager — FileCabinetWrapper → FileCabinetDesktop. Non-members see MemberUpgradeGate (same pattern as add-entity).
    /profile/cabinet—Redirects to /file-cabinet (ROUTES.PROFILE_CABINET aliases FILE_CABINET).
    /profile/sharedsubscriber+Full drag desktop of trustee shares. Subscriber-only users also see upgrade CTA membersUpgrade → membership with returnTo=/profile/shared.
    /profile/galleryAuth; member+Curate image/video → private | unlisted | public (MemberUpgradeGate for non-members).
    /{username}/imgPublicLightbox for visibility=public items.
    /{username}/playerPublicMood player (renamed from /songs; no legacy redirect). Private manage stays /profile/songs.

    ACL grants are immediate (no invite-accept). Max upload 25MB via getCabinetStorageConfig(). Publications use a separate co-author model — not File Cabinet ACL.

    Member-gated own manager · shared for all subscribers

    Own File Cabinet (/file-cabinet) and gallery manage (/profile/gallery) require member privileges (hasMemberPrivileges + MemberUpgradeGate). All subscribers (and above) open /profile/shared for trustee shares — they do not get an own desktop or upload surface until they upgrade to member.

    Why this matters for your clone

    Members need a place for docs, scans, and media that is not chat attachment chaos and not the Admin Wiki vault. File Cabinet is personal storage with trusted viewers, a shared inbox for collaborators, and a public gallery face — useful for marketplaces, service rings, and any clone where trust includes “show my work.”

    Who gets what (operator view)

    File Cabinet access gates

    Own file manager (member+)

    Members open /file-cabinet — upload, folders, trustees via ContactPicker, expandable tree + options rail. Non-members hit the membership upgrade gate.

    Shared for all subscribers

    Every subscriber+ uses /profile/shared for trustee shares (view/download desktop). An in-rail upgrade CTA points to membership for an own manager.

    Architecture

    Role gates in code (verified)

    SurfaceGate
    app/.../file-cabinet/page.tsxauth → hasMemberPrivileges else MemberUpgradeGate (returnTo = ROUTES.FILE_CABINET)
    app/.../profile/shared/page.tsxauth → hasRoleAtLeast(..., subscriber) else unauthorized
    app/.../profile/gallery/page.tsxauth → hasMemberPrivileges else MemberUpgradeGate
    app/.../profile/cabinet/page.tsxRedirect → ROUTES.FILE_CABINET

    Related documentation

    Related documentation

    RingFileBase (object storage API)

    Depends-on: cabinet uploads go through file() / RingBaseAdapter and getCabinetStorageConfig.

    Ring CDN (RingFileBase edge)

    Depends-on: public gallery items use stable CDN /files/{uuid} (+ _v_* derivatives).

    Public Profile Pages

    Next-step: public /img, /player, and /games surfaces hang off /{username}.

    Peer Games

    See-also: public /games availability is a sibling profile surface (not File Cabinet ACL).

    Public gallery

    Curate at /profile/gallery (member+); public items appear on /{username}/img via CDN URLs.

    Storage plane

    Objects live in RingFileBase / MinIO; gallery images can request derivativesProfile: gallery.

    Typical scenarios

    1. Vendor media kit — Member uploads PDFs and product photos on /file-cabinet; shares a folder with a trustee (view/download only, immediate grant).
    2. Subscriber collaborator — A subscriber without membership still opens /profile/shared, arranges trusted files on their desktop, and downloads — without owning a personal cabinet.
    3. Public portfolio strip — Owner marks selected images public → visitors open /{username}/img.
    4. Confidential vs shared — Empty trustee list = confidential; Share FsModal shows a live Trustees row (or “confidential”) and Make confidential to revoke all trustees.

    Operator checklist

    • Confirm migration 042_file_cabinet.sql applied (tables file_cabinet_*) and the app image includes File Cabinet UI.
    • Env: NEXT_PUBLIC_STORAGE_PROVIDER=ring_filebase (or equivalent), RINGBASE_API_URL, RINGBASE_PUBLIC_URL, RINGBASE_API_TOKEN — see RingFileBase.
    • Smoke as member: upload on /file-cabinet → Share trustees → second user (subscriber+) sees the item on /profile/shared.
    • Smoke as subscriber (non-member): /file-cabinet shows upgrade gate; /profile/shared still works for shared files; rail shows “Members get their own file manager”.
    • Smoke gallery: add image → set public → open /{username}/img.
    • Do not expect HMAC/TTL signed URLs — public delivery is stable CDN /files/{fileId} today.
    Actions (listOwnCabinetAction, upload, own desktop)
    requireMember()
    Actions (listSharedCabinetAction, shared desktop)requireSubscriber(); scope: 'own' still requires member

    UX shell (verified)

    FileCabinetWrapper is a thin client boundary (components/wrappers/file-cabinet-wrapper.tsx) that renders FileCabinetDesktop. The three-column shell (RingRightRailLayout + Davinci center pane) lives inside the desktop component.

    PieceRole
    Center paneBreadcrumbs + New folder / Upload (right) + icon workspace (no bordered desktop card)
    Right railTitle → expandable folders-only tree → options (DavinciGlassPanel, title = visible filename) → non-scrolling info panel
    Tree+/− expand; selected dir uses fully-rounded (rounded-[99px]) active surface; expand state in treeExpandedIds on the desktop document
    Desktop iconsVisible name SSOT in icons[].meta.filename (rename overwrites; no history); image icons use sync_thumb via download ?variant=
    Empty folderDelete control only when the folder has no children
    TrusteesOptions row + Share FsModal (live Trustees / confidential, inline ContactPicker, Make confidential when clearing trustees)
    LoadingFixed-height skeletons for trustees row, info meta, and image preview (no layout jump)
    Shared upgradeSubscriber-only + scope === 'shared' → t('membersUpgrade') CTA → /membership?returnTo=/profile/shared

    Module map

    PathRole
    features/file-cabinet/service.tsserver-only CRUD, ACL (incl. ancestor walk for inherited trustees), desktop, gallery
    features/file-cabinet/acl.tsowner | trustee (legacy editor → trustee)
    features/file-cabinet/constants.tsFILE_CABINET_DESKTOP_CHANNEL, FILE_CABINET_DOWNLOAD_PATH, MAX_FOLDER_DEPTH = 3
    features/file-cabinet/desktop-filename.tsVisible filename helpers
    features/file-cabinet/media-urls.tsSame-origin download URL helpers (?variant=)
    features/file-cabinet/components/*Desktop, tree, options, detail, share/rename FsModals, skeletons, gallery, public img
    app/_actions/file-cabinet.tsServer Actions + role gates
    app/api/file-cabinet/download/route.tsACL check → byte stream (inline=1, optional variant= for thumbs/webp)
    lib/storage/storage-config.tsgetCabinetStorageConfig() — 25MB + MIME allowlist
    components/file-tree/tree-helpers.tsShared tree helpers (Admin Wiki adapters)
    data/migrations/042_file_cabinet.sqlSchema (+ mirrored in data/schema.sql)

    ACL model

    RoleCapabilities
    ownerFull CRUD, set trustees via ContactPicker (setCabinetTrusteesAction)
    trusteeView / list / download; shared desktop layout; cannot mutate or upload

    Grants write to file_cabinet_acl immediately — no invite-accept flow. Listing ACL for the options rail walks ancestors so folder shares surface on nested files.

    Schema (JSONB collections)

    Tables (id + data JSONB): file_cabinet_nodes, file_cabinet_acl, file_cabinet_desktop, file_cabinet_gallery_items.

    Desktop document stores icon positions and treeExpandedIds for the folders-only expand/collapse tree. Per-folder layout cache keeps drag positions when navigating nested folders.

    createDoc id options

    Pass document id as createDoc(collection, data, { id }). Putting id only inside data lets PostgreSQLAdapter generateId() diverge from data.id.

    Storage upload (verified)

    Cabinet uploads use file().upload(...) with access: 'private', derivativesProfile: 'gallery' for images, and MIME/size checks from getCabinetStorageConfig() (images, PDF, Office, text/csv/md, zip, plus video/mp4 / video/webm).

    Public gallery items store the CDN URL from upload (storageUrl). There are no HMAC/TTL signed URLs in the current RingBaseAdapter path — delivery is {RINGBASE_PUBLIC_URL}/files/{fileId} (+ _v_* derivatives). Private downloads use the ACL proxy (/api/file-cabinet/download) with Content-Disposition from node.name / desktop meta.filename. Direct CDN /files/{uuid} still lacks Content-Disposition — see backlog.

    Desktop tunnel

    saveDesktop publishes to the member’s personal tunnel:

    Client listens via useTunnelChannel in file-cabinet-desktop.tsx.

    Wire-up steps

    1. 1

      Apply migration

      Ensure 042_file_cabinet.sql (and schema.sql parity) on the clone DB. Fresh installs pick up tables from data/schema.sql.

    2. 2

      Confirm storage env

      Reference RingFileBase: NEXT_PUBLIC_STORAGE_PROVIDER=ring_filebase, RINGBASE_API_URL, RINGBASE_PUBLIC_URL, RINGBASE_API_TOKEN.

    3. 3

      Call through Server Actions

      Prefer app/_actions/file-cabinet.ts from client UI — do not bypass ACL helpers in service.ts. Own list/upload requires member; shared desktop requires subscriber+; getDesktop / saveDesktop for scope: 'own' still require member privileges.

    4. 4

      Public img page

      app/[locale]/[username]/img/page.tsx loads listPublicGalleryByOwner(ownerId) and renders PublicProfileImgGallery. Only visibility=public items appear.

    Routes (constants)

    ConstantPath
    ROUTES.FILE_CABINET/file-cabinet (canonical own manager)
    ROUTES.PROFILE_CABINET/file-cabinet (deprecated alias; /profile/cabinet redirects)
    ROUTES.PROFILE_SHARED/profile/shared
    ROUTES.PROFILE_GALLERY/profile/gallery
    ROUTES.PUBLIC_PROFILE_IMG(username)/{username}/img
    ROUTES.PUBLIC_PROFILE_PLAYER(username)/{username}/player

    Private mood-player manage remains /profile/songs (ROUTES.PROFILE_SONGS). Peer /games is a sibling public-profile surface — not File Cabinet storage.

    SubscriptionConductor

    Same-workflow: member gate for /file-cabinet vs subscriber+ shared desktop.

    Tunnel Protocol

    Deep-dive: desktop icon sync uses publishToUserTunnel on channel file-cabinet:desktop-icons.

    Admin Wiki

    See-also: shared FileTree helpers and JSONB id+data collection pattern.

    Public gallery

    Curate at /profile/gallery (member+); public items appear on /{username}/img via CDN URLs.

    Storage plane

    Objects live in RingFileBase / MinIO; gallery images can request derivativesProfile: gallery.

    Typical scenarios

    1. Vendor media kit — Member uploads PDFs and product photos on /file-cabinet; shares a folder with a trustee (view/download only, immediate grant).
    2. Subscriber collaborator — A subscriber without membership still opens /profile/shared, arranges trusted files on their desktop, and downloads — without owning a personal cabinet.
    3. Public portfolio strip — Owner marks selected images public → visitors open /{username}/img.
    4. Confidential vs shared — Empty trustee list = confidential; Share FsModal shows a live Trustees row (or “confidential”) and Make confidential to revoke all trustees.

    Operator checklist

    • Confirm migration 042_file_cabinet.sql applied (tables file_cabinet_*) and the app image includes File Cabinet UI.
    • Env: NEXT_PUBLIC_STORAGE_PROVIDER=ring_filebase (or equivalent), RINGBASE_API_URL, RINGBASE_PUBLIC_URL, RINGBASE_API_TOKEN — see RingFileBase.
    • Smoke as member: upload on /file-cabinet → Share trustees → second user (subscriber+) sees the item on /profile/shared.
    • Smoke as subscriber (non-member): /file-cabinet shows upgrade gate; /profile/shared still works for shared files; rail shows “Members get their own file manager”.
    • Smoke gallery: add image → set public → open /{username}/img.
    • Do not expect HMAC/TTL signed URLs — public delivery is stable CDN /files/{fileId} today.
    Actions (listOwnCabinetAction, upload, own desktop)
    requireMember()
    Actions (listSharedCabinetAction, shared desktop)requireSubscriber(); scope: 'own' still requires member

    UX shell (verified)

    FileCabinetWrapper is a thin client boundary (components/wrappers/file-cabinet-wrapper.tsx) that renders FileCabinetDesktop. The three-column shell (RingRightRailLayout + Davinci center pane) lives inside the desktop component.

    PieceRole
    Center paneBreadcrumbs + New folder / Upload (right) + icon workspace (no bordered desktop card)
    Right railTitle → expandable folders-only tree → options (DavinciGlassPanel, title = visible filename) → non-scrolling info panel
    Tree+/− expand; selected dir uses fully-rounded (rounded-[99px]) active surface; expand state in treeExpandedIds on the desktop document
    Desktop iconsVisible name SSOT in icons[].meta.filename (rename overwrites; no history); image icons use sync_thumb via download ?variant=
    Empty folderDelete control only when the folder has no children
    TrusteesOptions row + Share FsModal (live Trustees / confidential, inline ContactPicker, Make confidential when clearing trustees)
    LoadingFixed-height skeletons for trustees row, info meta, and image preview (no layout jump)
    Shared upgradeSubscriber-only + scope === 'shared' → t('membersUpgrade') CTA → /membership?returnTo=/profile/shared

    Module map

    PathRole
    features/file-cabinet/service.tsserver-only CRUD, ACL (incl. ancestor walk for inherited trustees), desktop, gallery
    features/file-cabinet/acl.tsowner | trustee (legacy editor → trustee)
    features/file-cabinet/constants.tsFILE_CABINET_DESKTOP_CHANNEL, FILE_CABINET_DOWNLOAD_PATH, MAX_FOLDER_DEPTH = 3
    features/file-cabinet/desktop-filename.tsVisible filename helpers
    features/file-cabinet/media-urls.tsSame-origin download URL helpers (?variant=)
    features/file-cabinet/components/*Desktop, tree, options, detail, share/rename FsModals, skeletons, gallery, public img
    app/_actions/file-cabinet.tsServer Actions + role gates
    app/api/file-cabinet/download/route.tsACL check → byte stream (inline=1, optional variant= for thumbs/webp)
    lib/storage/storage-config.tsgetCabinetStorageConfig() — 25MB + MIME allowlist
    components/file-tree/tree-helpers.tsShared tree helpers (Admin Wiki adapters)
    data/migrations/042_file_cabinet.sqlSchema (+ mirrored in data/schema.sql)

    ACL model

    RoleCapabilities
    ownerFull CRUD, set trustees via ContactPicker (setCabinetTrusteesAction)
    trusteeView / list / download; shared desktop layout; cannot mutate or upload

    Grants write to file_cabinet_acl immediately — no invite-accept flow. Listing ACL for the options rail walks ancestors so folder shares surface on nested files.

    Schema (JSONB collections)

    Tables (id + data JSONB): file_cabinet_nodes, file_cabinet_acl, file_cabinet_desktop, file_cabinet_gallery_items.

    Desktop document stores icon positions and treeExpandedIds for the folders-only expand/collapse tree. Per-folder layout cache keeps drag positions when navigating nested folders.

    createDoc id options

    Pass document id as createDoc(collection, data, { id }). Putting id only inside data lets PostgreSQLAdapter generateId() diverge from data.id.

    Storage upload (verified)

    Cabinet uploads use file().upload(...) with access: 'private', derivativesProfile: 'gallery' for images, and MIME/size checks from getCabinetStorageConfig() (images, PDF, Office, text/csv/md, zip, plus video/mp4 / video/webm).

    Public gallery items store the CDN URL from upload (storageUrl). There are no HMAC/TTL signed URLs in the current RingBaseAdapter path — delivery is {RINGBASE_PUBLIC_URL}/files/{fileId} (+ _v_* derivatives). Private downloads use the ACL proxy (/api/file-cabinet/download) with Content-Disposition from node.name / desktop meta.filename. Direct CDN /files/{uuid} still lacks Content-Disposition — see backlog.

    Desktop tunnel

    saveDesktop publishes to the member’s personal tunnel:

    Client listens via useTunnelChannel in file-cabinet-desktop.tsx.

    Wire-up steps

    1. 1

      Apply migration

      Ensure 042_file_cabinet.sql (and schema.sql parity) on the clone DB. Fresh installs pick up tables from data/schema.sql.

    2. 2

      Confirm storage env

      Reference RingFileBase: NEXT_PUBLIC_STORAGE_PROVIDER=ring_filebase, RINGBASE_API_URL, RINGBASE_PUBLIC_URL, RINGBASE_API_TOKEN.

    3. 3

      Call through Server Actions

      Prefer app/_actions/file-cabinet.ts from client UI — do not bypass ACL helpers in service.ts. Own list/upload requires member; shared desktop requires subscriber+; getDesktop / saveDesktop for scope: 'own' still require member privileges.

    4. 4

      Public img page

      app/[locale]/[username]/img/page.tsx loads listPublicGalleryByOwner(ownerId) and renders PublicProfileImgGallery. Only visibility=public items appear.

    Routes (constants)

    ConstantPath
    ROUTES.FILE_CABINET/file-cabinet (canonical own manager)
    ROUTES.PROFILE_CABINET/file-cabinet (deprecated alias; /profile/cabinet redirects)
    ROUTES.PROFILE_SHARED/profile/shared
    ROUTES.PROFILE_GALLERY/profile/gallery
    ROUTES.PUBLIC_PROFILE_IMG(username)/{username}/img
    ROUTES.PUBLIC_PROFILE_PLAYER(username)/{username}/player

    Private mood-player manage remains /profile/songs (ROUTES.PROFILE_SONGS). Peer /games is a sibling public-profile surface — not File Cabinet storage.

    SubscriptionConductor

    Same-workflow: member gate for /file-cabinet vs subscriber+ shared desktop.

    Tunnel Protocol

    Deep-dive: desktop icon sync uses publishToUserTunnel on channel file-cabinet:desktop-icons.

    Admin Wiki

    See-also: shared FileTree helpers and JSONB id+data collection pattern.

    Public gallery

    Curate at /profile/gallery (member+); public items appear on /{username}/img via CDN URLs.

    Storage plane

    Objects live in RingFileBase / MinIO; gallery images can request derivativesProfile: gallery.

    Typical scenarios

    1. Vendor media kit — Member uploads PDFs and product photos on /file-cabinet; shares a folder with a trustee (view/download only, immediate grant).
    2. Subscriber collaborator — A subscriber without membership still opens /profile/shared, arranges trusted files on their desktop, and downloads — without owning a personal cabinet.
    3. Public portfolio strip — Owner marks selected images public → visitors open /{username}/img.
    4. Confidential vs shared — Empty trustee list = confidential; Share FsModal shows a live Trustees row (or “confidential”) and Make confidential to revoke all trustees.

    Operator checklist

    • Confirm migration 042_file_cabinet.sql applied (tables file_cabinet_*) and the app image includes File Cabinet UI.
    • Env: NEXT_PUBLIC_STORAGE_PROVIDER=ring_filebase (or equivalent), RINGBASE_API_URL, RINGBASE_PUBLIC_URL, RINGBASE_API_TOKEN — see RingFileBase.
    • Smoke as member: upload on /file-cabinet → Share trustees → second user (subscriber+) sees the item on /profile/shared.
    • Smoke as subscriber (non-member): /file-cabinet shows upgrade gate; /profile/shared still works for shared files; rail shows “Members get their own file manager”.
    • Smoke gallery: add image → set public → open /{username}/img.
    • Do not expect HMAC/TTL signed URLs — public delivery is stable CDN /files/{fileId} today.
    Actions (listOwnCabinetAction, upload, own desktop)
    requireMember()
    Actions (listSharedCabinetAction, shared desktop)requireSubscriber(); scope: 'own' still requires member

    UX shell (verified)

    FileCabinetWrapper is a thin client boundary (components/wrappers/file-cabinet-wrapper.tsx) that renders FileCabinetDesktop. The three-column shell (RingRightRailLayout + Davinci center pane) lives inside the desktop component.

    PieceRole
    Center paneBreadcrumbs + New folder / Upload (right) + icon workspace (no bordered desktop card)
    Right railTitle → expandable folders-only tree → options (DavinciGlassPanel, title = visible filename) → non-scrolling info panel
    Tree+/− expand; selected dir uses fully-rounded (rounded-[99px]) active surface; expand state in treeExpandedIds on the desktop document
    Desktop iconsVisible name SSOT in icons[].meta.filename (rename overwrites; no history); image icons use sync_thumb via download ?variant=
    Empty folderDelete control only when the folder has no children
    TrusteesOptions row + Share FsModal (live Trustees / confidential, inline ContactPicker, Make confidential when clearing trustees)
    LoadingFixed-height skeletons for trustees row, info meta, and image preview (no layout jump)
    Shared upgradeSubscriber-only + scope === 'shared' → t('membersUpgrade') CTA → /membership?returnTo=/profile/shared

    Module map

    PathRole
    features/file-cabinet/service.tsserver-only CRUD, ACL (incl. ancestor walk for inherited trustees), desktop, gallery
    features/file-cabinet/acl.tsowner | trustee (legacy editor → trustee)
    features/file-cabinet/constants.tsFILE_CABINET_DESKTOP_CHANNEL, FILE_CABINET_DOWNLOAD_PATH, MAX_FOLDER_DEPTH = 3
    features/file-cabinet/desktop-filename.tsVisible filename helpers
    features/file-cabinet/media-urls.tsSame-origin download URL helpers (?variant=)
    features/file-cabinet/components/*Desktop, tree, options, detail, share/rename FsModals, skeletons, gallery, public img
    app/_actions/file-cabinet.tsServer Actions + role gates
    app/api/file-cabinet/download/route.tsACL check → byte stream (inline=1, optional variant= for thumbs/webp)
    lib/storage/storage-config.tsgetCabinetStorageConfig() — 25MB + MIME allowlist
    components/file-tree/tree-helpers.tsShared tree helpers (Admin Wiki adapters)
    data/migrations/042_file_cabinet.sqlSchema (+ mirrored in data/schema.sql)

    ACL model

    RoleCapabilities
    ownerFull CRUD, set trustees via ContactPicker (setCabinetTrusteesAction)
    trusteeView / list / download; shared desktop layout; cannot mutate or upload

    Grants write to file_cabinet_acl immediately — no invite-accept flow. Listing ACL for the options rail walks ancestors so folder shares surface on nested files.

    Schema (JSONB collections)

    Tables (id + data JSONB): file_cabinet_nodes, file_cabinet_acl, file_cabinet_desktop, file_cabinet_gallery_items.

    Desktop document stores icon positions and treeExpandedIds for the folders-only expand/collapse tree. Per-folder layout cache keeps drag positions when navigating nested folders.

    createDoc id options

    Pass document id as createDoc(collection, data, { id }). Putting id only inside data lets PostgreSQLAdapter generateId() diverge from data.id.

    Storage upload (verified)

    Cabinet uploads use file().upload(...) with access: 'private', derivativesProfile: 'gallery' for images, and MIME/size checks from getCabinetStorageConfig() (images, PDF, Office, text/csv/md, zip, plus video/mp4 / video/webm).

    Public gallery items store the CDN URL from upload (storageUrl). There are no HMAC/TTL signed URLs in the current RingBaseAdapter path — delivery is {RINGBASE_PUBLIC_URL}/files/{fileId} (+ _v_* derivatives). Private downloads use the ACL proxy (/api/file-cabinet/download) with Content-Disposition from node.name / desktop meta.filename. Direct CDN /files/{uuid} still lacks Content-Disposition — see backlog.

    Desktop tunnel

    saveDesktop publishes to the member’s personal tunnel:

    Client listens via useTunnelChannel in file-cabinet-desktop.tsx.

    Wire-up steps

    1. 1

      Apply migration

      Ensure 042_file_cabinet.sql (and schema.sql parity) on the clone DB. Fresh installs pick up tables from data/schema.sql.

    2. 2

      Confirm storage env

      Reference RingFileBase: NEXT_PUBLIC_STORAGE_PROVIDER=ring_filebase, RINGBASE_API_URL, RINGBASE_PUBLIC_URL, RINGBASE_API_TOKEN.

    3. 3

      Call through Server Actions

      Prefer app/_actions/file-cabinet.ts from client UI — do not bypass ACL helpers in service.ts. Own list/upload requires member; shared desktop requires subscriber+; getDesktop / saveDesktop for scope: 'own' still require member privileges.

    4. 4

      Public img page

      app/[locale]/[username]/img/page.tsx loads listPublicGalleryByOwner(ownerId) and renders PublicProfileImgGallery. Only visibility=public items appear.

    Routes (constants)

    ConstantPath
    ROUTES.FILE_CABINET/file-cabinet (canonical own manager)
    ROUTES.PROFILE_CABINET/file-cabinet (deprecated alias; /profile/cabinet redirects)
    ROUTES.PROFILE_SHARED/profile/shared
    ROUTES.PROFILE_GALLERY/profile/gallery
    ROUTES.PUBLIC_PROFILE_IMG(username)/{username}/img
    ROUTES.PUBLIC_PROFILE_PLAYER(username)/{username}/player

    Private mood-player manage remains /profile/songs (ROUTES.PROFILE_SONGS). Peer /games is a sibling public-profile surface — not File Cabinet storage.

    SubscriptionConductor

    Same-workflow: member gate for /file-cabinet vs subscriber+ shared desktop.

    Tunnel Protocol

    Deep-dive: desktop icon sync uses publishToUserTunnel on channel file-cabinet:desktop-icons.

    Admin Wiki

    See-also: shared FileTree helpers and JSONB id+data collection pattern.