# Server Architecture ## Request Flow ```text HTTP request -> middleware -> handler -> service -> repository and storage ``` ## Layer Responsibilities | Layer | Packages | Responsibility | |-------|----------|----------------| | CLI | `cmd` | Register Cobra commands and start the application | | Composition | `internal/app` | Open runtime resources and construct services | | HTTP | `internal/server`, `internal/handler`, `internal/middleware`, `internal/api` | Route requests, authenticate principals, map DTOs, and write HTTP responses | | Business | `internal/service`, `internal/model` | Validate operations, enforce authorization, and represent domain results and errors | | Data | `internal/repository`, `internal/storage` | Persist metadata and file content | | Infrastructure | `internal/config`, `internal/logging`, `internal/auth` | Load configuration, create loggers, and provide token and password primitives | | Test support | `internal/testutil` | Prepare privileged fixtures through test-only helpers | Handlers define REST request and response DTOs. `internal/api` maps domain error kinds to HTTP status codes and error bodies. Services use protocol-neutral domain types and errors. Each service constructor accepts the repository capabilities required by its use cases. Repository query capabilities return domain records. Mutation capabilities use operation-specific parameter types, select writable columns explicitly, and apply the ownership and lifecycle predicates of the operation. Storage backends manage staged and permanent file content. Repository records hold storage paths and file metadata. ## Runtime Composition `app.Bootstrap` opens the configured database, runs migrations, creates repositories and local storage, constructs the services, and returns `app.WebApp`. `server.NewRouter` creates a Gin engine and registers the public and protected route groups. `cmd/serve.go` loads configuration, initializes logging, bootstraps the application, and runs graceful shutdown. `app.WebApp` owns the database lifecycle and exposes the services, storage backend, configuration, and build metadata required by HTTP setup. ## HTTP Boundaries Global middleware runs in this order: 1. Request ID 2. Gin access logger 3. Gin recovery Protected routes use `AuthRequired` to authenticate an access token and attach a service principal. Administrator routes add `AdminRequired` to verify the principal's administrator flag. | Route group | Access | Capability | |-------------|--------|------------| | `/api/v1/version` | Public | Build metadata | | `/api/v1/auth` | Public | Registration, login, refresh, and logout | | `/api/v1/account` | Authenticated | Account and application passkeys | | `/api/v1/files` | Authenticated | File and directory operations | | `/api/v1/admin/users` | Administrator | User listing, lookup, and deletion | The route declarations in `internal/server/routes_public.go` and `internal/server/routes_protected.go` are the source of truth for individual methods and paths. ## Persistence Commands | Area | Mutation contract | |------|-------------------| | Users | Registration accepts `RegisteredUserParams` and creates an active non-admin user. Administration exposes a separate deletion command. | | Sessions | Refresh sessions are created, atomically consumed, revoked by token hash, or removed by maintenance operations. | | Files | Upload, directory creation, metadata update, and soft deletion use distinct parameter types and commands. | | Credentials | Application passkeys have explicit creation, usage-recording, and revocation operations. | Metadata updates can write `name`, `parent_id`, and GORM-managed `updated_at`. File ownership and active-state predicates scope user mutations. ## Transaction Boundaries File child creation, movement, and directory deletion use one hierarchy transaction protocol. PostgreSQL locks active owned source and parent rows in sorted ID order. SQLite adds `_txlock=immediate` to the configured DSN and reserves the write transaction before reading hierarchy state. Refresh rotation locks, returns, and deletes the session in one transaction. A refresh session can issue one replacement token pair. Uploads write to a staging path before promotion. The service promotes validated content before creating the active file record and attempts to remove promoted content when the database create fails. ## Enforced Architecture Constraints `internal/architecture_test.go` enforces these boundaries: - `internal/model` and `internal/service` do not import `net/http`, Gin, or `internal/api`. - `internal/handler` and `internal/middleware` do not import `internal/repository`. - Repository implementations do not call GORM `Save`; mutation methods select writable columns explicitly.