Skip to content

System Overview

Architecture Diagram

Module Responsibilities

platform-api

Shared Dubbo interface definitions that all modules depend on:

  • BlockChainService - Blockchain operations
  • DistributedStorageService - Storage operations
  • Common DTOs and response types

platform-backend

Multi-module backend service (Dubbo Consumer):

SubmoduleResponsibility
backend-webREST controllers, JWT filters, rate limiting, CORS
backend-serviceBusiness logic, Saga orchestration, Outbox publishing
backend-daoMyBatis Plus mappers, entities, VOs
backend-apiInternal API interfaces
backend-commonUtilities, constants, annotations

platform-fisco

Blockchain integration service (Dubbo Provider):

  • Smart contract interaction (Storage.sol, Sharing.sol)
  • Multi-chain adapters (Local FISCO, BSN FISCO, Besu)
  • Certificate management

platform-storage

Distributed storage service (Dubbo Provider):

  • Multi-node S3 client management
  • Fault domain management
  • Consistent hashing and rebalancing
  • File encryption/decryption

Core Business Flow

File Upload & Attestation

File Download Flow

Download Strategy Comparison:

StrategyUse CaseCharacteristics
Memory sinkFiles up to 64 MiBGuarded Blob fallback; total output cannot exceed the hard ceiling
File-system stream sinkFiles above 64 MiBRequires File System Access API plus Streams; bounded reader writes directly to the chosen file
Unsupported browserFiles above 64 MiB without a stream sinkFails closed with a browser-capability message; there is no unbounded backend-proxy fallback

The bounded reader accepts explicit NONE, legacy v1 AEAD, and framed AEAD v2 formats for owned, public-share, and authenticated-share downloads. Legacy ciphertext parts are capped at about 80 MiB + 4 KiB, every file at 10,000 parts, and each response chunk at 1 MiB. A fetch has at most three attempts; the first 401/403 may refresh metadata once and resume the current part only after exact file/version/manifest/suite/access and stable-part identity comparison. A second expiry or any drift fails closed; only 5xx responses receive finite retry. Every length, order, hash, key, decrypt, cancellation, or sink error aborts the sink.

File Sharing Flow

Friend Sharing

Share Type Comparison:

TypeAccess ControlExpiryCharacteristics
Public ShareNoneConfigurableAnyone can access via link
Private SharePasswordConfigurablePassword required for access
Friend ShareFriendshipPermanentOnly specified friend can view, supports read status

Saga Compensation Flow

StepForward ActionCompensation
PENDINGInitialize-
S3_UPLOADINGStore chunksClean stored chunks
S3_UPLOADEDChunks storedDelete S3 files
CHAIN_STORINGBlockchain attestationMark chain record deleted
COMPLETEDCommit-

Compensation Strategy: Exponential backoff (initial 1s, max 5 retries), then manual queue.

Saga State Machine

The FileSagaOrchestrator manages the complete state machine:

Transactional Outbox Pattern

RecordPlatform uses the Outbox pattern for reliable event publishing to RabbitMQ.

How It Works

Components

ComponentResponsibility
OutboxServiceAppends events within business transaction
OutboxPublisherBackground polling and publishing (2s interval)
outbox_event tablePersistent event store with tenant isolation

Guarantees

  • At-least-once delivery: Events survive broker unavailability
  • Transactional consistency: Event created in same DB transaction as business data
  • Tenant-aware polling: Each tenant's events processed independently

Configuration

yaml
outbox:
  publisher:
    batch-size: 100
    poll-interval-ms: 2000
    max-retries: 5
  cleanup:
    sent-retention-days: 7
    failed-retention-days: 30
    cron: 0 0 3 * * ?

CQRS Architecture

File module uses Command Query Responsibility Segregation:

Virtual Thread Async Methods

Query service provides async methods using Java 21 Virtual Threads:

  • getUserFilesListAsync()
  • getFileAddressAsync()

File Version Chain

The file version chain allows a single logical file to have multiple historical versions, linked together in a chain structure. See the dedicated File Version Chain page for detailed design.

Core Fields

FieldDescription
version_group_idShared group ID across all versions of the same logical file
parent_version_idParent version ID (null for first version)
is_latestMarks the latest version in the chain (only one true per group)

Version API

  • GET /api/v1/files/{id}/versions — List the version chain for a file
  • POST /api/v1/files/{id}/versions — Mark an existing file as parent for a new upload (via targetFileId)

Multi-tenancy

Isolation Strategy

LayerIsolation Method
Databasetenant_id field, MyBatis auto-inject
RedisKey prefix tenant:{tenantId}:
S3 StoragePath /{tenantId}/{userId}/
DubboContext propagation TenantContext

Tenant Context Control

@TenantScope annotation for declarative tenant isolation:

java
// Cross-tenant query (scheduled tasks)
@TenantScope(ignoreIsolation = true)
@Scheduled(cron = "0 0 3 * * ?")
public void cleanupDeletedFiles() { ... }

// Switch to specific tenant
@TenantScope(tenantId = 1)
public void migrateDataForTenant() { ... }

Real-time Notifications (SSE)

Server-Sent Events provide real-time updates to connected clients.

Multi-Connection Architecture

The system supports multiple simultaneous connections per user:

Connection Configuration

ParameterDefaultDescription
Max connections per user5Oldest connection closed when exceeded
Heartbeat interval30sKeep-alive signal
Connection timeout30mAuto-close after inactivity
Reconnect delay (base)2sBase delay for exponential client reconnect
Reconnect delay (max)30sUpper bound for reconnect backoff
Max reconnect attempts5Falls back to manual reconnect after limit

Event Types

EventPayloadDescription
connected{ connectionId }Initial connection confirmation
notification{ title, content }General notification
message-received{ conversationId, preview }New message in conversation
file-record-success{ fileName, fileHash, status }File attestation success notification
file-record-failed{ fileName, status, reason }File attestation failure notification
announcement-published{ id, title }System announcement
ticket-updated{ ticketId, status }Ticket status change
badge-update{ unreadMessages, tickets }UI badge count update
friend-request{ requesterName, ... }New friend request
friend-accepted{ friendName, ... }Friend request accepted
friend-share{ sharerName, fileCount, ... }Friend file sharing
audit-alert{ type, message, details, severity }Audit anomaly alert (admin/monitor)
integrity-alert{ alertType, fileHash, details }Storage integrity anomaly alert (admin)

SSE Authentication Handshake

SSE connections use a short-lived one-time token:

  1. Call POST /api/v1/auth/tokens/sse with regular JWT auth
  2. Connect via GET /api/v1/sse/connect?token={sseToken}&x-tenant-id={tenantHint}

GET /api/v1/sse/connect is publicly exposed in Spring Security but still requires valid short-lived token authentication. The tenant value is only a Redis namespace hint; validated token tenant/user/role data is authoritative.

Monitoring Capacity Semantics

System monitoring now includes:

  • GET /api/v1/system/storage-capacity

Response shape includes:

  • Cluster totals: totalCapacityBytes, usedCapacityBytes, availableCapacityBytes
  • Data quality flags: degraded, source
  • Detailed aggregates: nodes[], domains[]

GET /api/v1/system/stats now uses this capacity result for totalStorage. Only when Dubbo capacity RPC fails does it fall back to totalFiles * 1MB, logging marker MONITOR_STORAGE_CAPACITY_FALLBACK.

Download Strategy (Frontend)

Frontend selects a bounded sink by file size and browser capability. The 64 MiB threshold is a hard memory ceiling, not a tuning suggestion. Larger files require File System Access API plus Streams; unsupported browsers are rejected before transfer rather than routed through an unbounded proxy.

Default thresholds (platform-frontend/src/lib/utils/fileSize.ts):

Threshold ConstantDefault
MAX_SAFE_INMEMORY_SIZE / LARGE_FILE_WARNING_THRESHOLD64 MiB
STREAMING_RECOMMENDED_THRESHOLD500 MiB
VERY_LARGE_FILE_THRESHOLD2 GiB
MAX_DOWNLOADABLE_SIZE100 GiB

Owned downloads require an active chunk manifest and expose structured governance fields. Missing metadata returns FILE_RECORD_ERROR with ManifestErrorDetail under data.detail; clients use manifestStatus, manifestClassification, manifestErrorCode, and legacyDownloadAllowed instead of parsing message. See Chunk Manifest and Legacy Governance.

Quota Governance

The platform enforces per-user and per-tenant storage quotas with two enforcement modes:

  • SHADOW (default): Quota violations are logged and alerted but uploads are not blocked. Use during rollout to observe impact.
  • ENFORCE: Uploads exceeding quota are rejected with 50013 QUOTA_EXCEEDED.

Rollout Strategy:

PropertyDescription
quota.enforcement-modeGlobal mode: SHADOW or ENFORCE
quota.rollout.strategyTENANT_WHITELIST — non-empty whitelist limits ENFORCE to listed tenants
quota.rollout.enforce-tenant-whitelistComma-separated tenant IDs for ENFORCE mode; empty means all tenants when global mode is ENFORCE
quota.rollout.force-shadowOverride to force SHADOW for all tenants

Reconciliation: A scheduled job (quota.reconcile.cron, default every 30 minutes) recalculates usage snapshots to correct any drift between cached and actual usage.

API Endpoints:

  • GET /api/v1/files/quota — Current user quota status
  • POST /api/v1/admin/quota/rollout/audits — Write rollout audit record (admin)
  • GET /api/v1/admin/quota/rollout/audits — Query rollout audit record (admin)

Batch Download

The frontend supports selecting multiple files for batch download:

  1. Select: User selects files from the file list
  2. Parallel Download: Files are downloaded concurrently (up to configurable concurrency limit)
  3. Auto-Retry: Failed downloads are automatically retried
  4. Metrics Report: After completion, the frontend reports batch quality metrics via POST /api/v1/files/download-batches/report

The report payload includes total files, successful/failed counts, retry count, total duration, and error type breakdown — used for backend quality observability.

Keyword Search Modes

File queries (GET /api/v1/files) support a keywordMode parameter that controls how the keyword is matched:

ModeFile Name MatchingFile Hash MatchingUse Case
FUZZY (default)LIKE %keyword%LIKE %keyword%General search
PREFIXLIKE keyword%Exact matchFaster indexed lookup
EXACT_HASHNot searchedExact match onlyDirect hash lookup
AUTODepends on keywordDepends on keywordSmart detection

AUTO mode inspects the keyword: if it matches the hex hash pattern (/^[0-9a-fA-F]{32,128}$/), it resolves to EXACT_HASH; otherwise it resolves to PREFIX.

Frontend Leader Election

For multi-tab scenarios, frontend uses BroadcastChannel for leader election:

  • Leader tab: Maintains single SSE connection
  • Follower tabs: Receive events via BroadcastChannel
  • Failover: Auto-elect new leader when leader tab closes

This prevents multiple SSE connections from same browser, reducing server load.

Released under the Apache 2.0 License.