DocsServer features
External storage
Use Dilnaka Storage for durable user files, rather than relying on the filesystem inside a Docker service.
A container's writable filesystem is not a durable upload store. A deployment
can replace the container, a restart can erase its writable layer, and multiple
replicas do not share it. public/ is therefore for application
assets baked into the image, never for files users upload.
Rahti receives and validates the upload; Dilnaka Storage keeps the durable copy. Its Rust SDK supports ordinary uploads, automatic multipart transfer for large files, and temporary access URLs.
When external storage is required
| Keep here | Store in Dilnaka Storage |
|---|---|
Compiled CSS, JavaScript, icons, and application images in public/ | User avatars, attachments, exports, media, and any file that must survive a deploy. |
| Short-lived upload staging while the request is being processed | The final object, addressed by its returned file id or key. |
| A local development fixture | A file shared across replicas, environments, or future deployments. |
Install the Rust SDK
Add the client to the Rahti application that uploads files. It is not a Rahti framework dependency: projects without uploads do not need to compile or ship it.
[dependencies]
dilnaka = "0.0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }The SDK requires Rust 1.85 or newer. See the Dilnaka Rust SDK reference for its full API and current release notes.
Configure the service, not the repository
DILNAKA_API_KEY is a server credential. Keep it in the git-ignored .env file
for development and set the same variable in the deployment's service environment.
Never put it in rahti.config.json, public/,
browser JavaScript, or an RPC response.
# .env — local development only; never commit this value
DILNAKA_API_KEY=dlk_dev_your_api_key_here
# Optional SDK controls, in seconds and bytes
DILNAKA_TIMEOUT=60
DILNAKA_MULTIPART_THRESHOLD=104857600
DILNAKA_UPLOAD_TIMEOUT=300Upload flow
- Receive the file through a Rahti RPC using
RpcFileorRpcUpload. - Require a session where appropriate and authorize the caller for the target record.
- Enforce size, file type, and content validation; filenames and MIME types are untrusted metadata.
- Use a server-controlled temporary path only while transferring the accepted file to Dilnaka Storage.
- Persist Dilnaka's returned file id/key with the application record, then request a temporary access URL when it is read.
use dilnaka::{Dilnaka, UploadOptions};
// Call this after the application has validated and placed a file in a
// server-controlled temporary location. The temporary path is staging only;
// Dilnaka Storage is the durable copy.
async fn store_avatar(staging_path: &std::path::Path) -> dilnaka::Result<String> {
let client = Dilnaka::from_env()?;
let uploaded = client
.upload_with_options(
staging_path,
UploadOptions::default()
.folder("avatars")
.content_type("image/png"),
)
.await?;
Ok(uploaded.id)
}Direct and large uploads
For ordinary uploads, the server can transfer the validated staging file through the SDK, as above. For large browser uploads, the SDK can create presigned and multipart uploads, allowing bytes to go to storage without making the Rahti container retain the complete file. Keep the API key on the server, restrict the requested filename, type, size and folder before issuing a presign, and verify completion before treating the object as available.
Use get_file_access_url to request a temporary download or
preview URL for private files; do not construct object URLs from user-controlled
keys. The SDK's upload_with_options automatically changes to a
resumable multipart flow at its configured threshold.
