How Zakirullin Files Enforces Storage Quota Limits Per User

Zakirullin Files limits user data by validating file write operations against a configurable per-user quota, while allowing administrators to exempt specific user IDs from storage restrictions.

The zakirullin/files.md repository provides a markdown file server with built-in storage management capabilities. The quota system operates through a three-layer architecture: configuration loading, user-specific exemptions, and runtime write validation.

Configuring the Default Quota Limit

The storage quota configuration resides in server/config/config.go, where the application defines default limits and environment variable overrides.

// server/config/config.go
StorageQuotaKB    int64  `default:"1024" envconfig:"STORAGE_QUOTA_KB"` // 1 MiB
UnlimitedQuotaIDs string `envconfig:"UNLIMITED_QUOTA_IDS"`

The StorageQuotaKB field defaults to 1024 (1 MiB) but can be customized at startup using the STORAGE_QUOTA_KB environment variable. The configuration loads via config.LoadBotConfig() during server initialization.

To set a custom quota before starting the server:

export STORAGE_QUOTA_KB=5120   # 5 MiB per user

go run ./cmd/server

Exempting Users from Quota Restrictions

Administrators can designate specific users as unlimited by populating the UnlimitedQuotaIDs environment variable with comma-separated user identifiers.

The exemption logic lives in server/fs/quota.go:

// server/fs/quota.go
func isUnlimitedQuota(userID int64, unlimitedIDs string) bool { … }

When initializing a user's filesystem in server/fs/fs.go, the newUserFS function checks this status and sets quotaKB to 0 for unlimited users, effectively disabling storage checks:

// server/fs/fs.go (newUserFS)
quotaKB := config.ServerCfg.StorageQuotaKB
if isUnlimitedQuota(userID, config.ServerCfg.UnlimitedQuotaIDs) {
    quotaKB = 0
}

To exempt specific users:

export UNLIMITED_QUOTA_IDS="1234,5678"
go run ./cmd/server

Runtime Quota Validation During Write Operations

The enforcement mechanism activates during every file write through the Write method in server/fs/fs.go. The system calculates storage consumption lazily using storageUsed and caches results in storageQuotaCache.

Before committing data, the code invokes checkQuota from server/fs/quota.go:

// server/fs/quota.go
func checkQuota(rootPath string, backend afero.Fs, quotaKB int64, contentSize int64) error {
    if quotaKB <= 0 { return nil }                          // unlimited
    if storageUsed(rootPath, backend)+contentSize > quotaKB*1024 {
        return ErrQuotaExceeded
    }
    return nil
}

The Write method calculates the size delta (newSize - oldSize) and validates against the quota:

// server/fs/fs.go (Write)
newSize := int64(len(content))
if err := checkQuota(fs.rootPath, fs.backend, fs.quotaKB, newSize-oldSize); err != nil {
    return err
}

If validation passes, recordQuotaUsage updates the cached total. If the quota would be exceeded, the operation aborts immediately with ErrQuotaExceeded.

Handling Quota Exceeded Errors

When applications attempt to write data that breaches the configured limit, the filesystem returns a specific error that can be handled programmatically:

package main

import (
    "fmt"
    "strings"
    "github.com/zakirullin/files.md/server/fs"
)

func main() {
    userFS, err := fs.NewUserFS(1234)
    if err != nil {
        panic(err)
    }

    content := strings.Repeat("x", 2*1024*1024) // 2 MiB
    if err := userFS.Write(fs.DirJournal, "BigNote.md", content); err != nil {
        fmt.Printf("Write failed: %v\n", err) // ErrQuotaExceeded if quota is 1 MiB
    } else {
        fmt.Println("Write succeeded")
    }
}

Summary

  • Configuration: Default 1 MiB quotas are defined in server/config/config.go and overridable via STORAGE_QUOTA_KB.
  • Exemptions: Specific user IDs listed in UNLIMITED_QUOTA_IDS bypass all restrictions through the isUnlimitedQuota helper.
  • Enforcement: The checkQuota function in server/fs/quota.go validates every write operation against cached storage totals, aborting with ErrQuotaExceeded when limits are breached.
  • Delta Calculation: Quota checks account for file modifications by comparing new content size against existing file size.

Frequently Asked Questions

How do I increase the storage quota for all users?

Set the STORAGE_QUOTA_KB environment variable to the desired kilobyte limit before starting the server. For example, export STORAGE_QUOTA_KB=10240 sets a 10 MiB quota per user. The configuration loads at startup via config.LoadBotConfig().

Can I give specific users unlimited storage while keeping limits for others?

Yes. Add the user IDs to the UNLIMITED_QUOTA_IDS environment variable as comma-separated values. The newUserFS function in server/fs/fs.go sets quotaKB = 0 for these users, causing checkQuota to skip validation entirely.

What happens when a user exceeds their storage quota?

The Write method returns ErrQuotaExceeded immediately before writing data to disk. The operation aborts without modifying the filesystem, and the application receives the error to handle appropriately.

How does the system calculate current storage usage?

The storageUsed function calculates consumption by walking the user's root directory, with results cached in storageQuotaCache to minimize filesystem traversal during frequent write operations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →