# Fileserver upgrade guide ## Enabling the capability The Fileserver ships off. Nothing is registered — no endpoint, no thread pool, no metric — until it is enabled explicitly. ```yaml ca-skeleton: fileserver: enabled: true instance-id: ${HOSTNAME} default-namespace: default observability: fingerprint-key: ${FILESERVER_FINGERPRINT_KEY} ``` `instance-id` must be unique per instance: it is the writer-lease owner, and two nodes sharing one would both believe they hold the same lease. `fingerprint-key` is required and has no default. Startup fails without it rather than falling back to an unkeyed digest, which would be reversible for an enumerable identifier space. ## Optional surfaces Each is a separate switch, and each defaults to off: ```yaml ca-skeleton: fileserver: admin: enabled: false # management plane; intended for a management port tus: enabled: false # tus 1.0 Stable httpbis-draft12: enabled: false # Experimental; unratified, may change without notice nginx: enabled: false # front-proxy delegation; needs a validated internal location ``` ## Database schema The metadata schema is installed as a capability migration and starts inactive: ``` V1__create_fileserver_metadata.sql → capability_schema_registry: jpa-fileserver-metadata-v1 ``` Activate it deliberately. Enabling the capability without an activated schema fails at startup rather than at the first upload. ## Choosing a publish mode | Mode | When | |---|---| | `atomic-move-preferred` | Default. Uses an atomic rename when the probe proves one, else a metadata pointer. | | `atomic-move-required` | Fail closed. Refuses to start on storage that cannot prove an atomic move. | | `metadata-pointer` | For storage without atomic rename; publication is the metadata commit. | Pick `atomic-move-required` when the storage is certified and you want a misconfiguration to surface at boot rather than at publish time. ## Behaviour that will surprise you - **A delete answers `202`, not `204`, when content still exists.** The file is already unreadable; the physical reclaim is deferred. Treating `202` as a failure will produce spurious retries. - **A batch upload answers `200` even when parts failed.** The batch is explicitly non-atomic, and a single status could not report a partial outcome honestly. Read `results[].problem`. - **An ambiguous failure must not be retried.** Check `"ambiguous": true` in the problem document. - **`If-Match` takes the strong ETag, not a version number.** A client can only assert about the representation it was actually served. - **Inline rendering is refused for scriptable types** even when the caller asks for it. ## Verifying an upgrade ```bash cd src ./gradlew verifyCleanArchitectureDependencies --console=plain ./gradlew :application-core:check :adapter:inbound:web:check \ :adapter:outbound:fileserver:check --console=plain ./gradlew :app-bootstrap:test --tests '*Fileserver*' --console=plain ```