Skip to content

Handle File Uploads

This guide shows you how to enable and manage file uploads on your Teyaotlani server.

Prerequisites

Enable uploads in configuration

Add the [upload] section to your config.toml:

[upload]
enabled = true
upload_dir = "./uploads"
max_upload_size = 10485760  # 10 MB
enable_delete = false

Create the upload directory:

mkdir uploads

Upload files with the CLI

Use the upload command to send files to a server:

# Upload string content
teyaotlani upload spartan://localhost:3000/message.txt -c "Hello, world!"

# Upload a file
teyaotlani upload spartan://localhost:3000/document.gmi -f ./mydoc.gmi

Upload files with Python

Use the SpartanClient to upload programmatically:

import asyncio
from teyaotlani import SpartanClient

async def upload_file():
    async with SpartanClient() as client:
        # Upload string content
        response = await client.upload(
            "spartan://localhost:3000/hello.txt",
            b"Hello, world!"
        )
        print(f"Status: {response.status}")

        # Upload binary file
        with open("image.png", "rb") as f:
            data = f.read()
        response = await client.upload(
            "spartan://localhost:3000/image.png",
            data
        )
        print(f"Upload status: {response.status}")

asyncio.run(upload_file())

Or use the convenience function:

from teyaotlani import upload

response = asyncio.run(upload(
    "spartan://localhost:3000/file.txt",
    b"File contents"
))

Set upload size limits

Configure maximum upload size to prevent abuse:

[upload]
enabled = true
upload_dir = "./uploads"
max_upload_size = 5242880  # 5 MB

Uploads exceeding this size will receive a 4 (client error) response.

Enable file deletion

The Spartan protocol supports deletion by uploading zero bytes. Enable this feature:

[upload]
enabled = true
upload_dir = "./uploads"
enable_delete = true

Delete a file:

# Send zero-byte upload to delete
teyaotlani upload spartan://localhost:3000/old-file.txt -c ""

Or in Python:

response = await client.upload(
    "spartan://localhost:3000/old-file.txt",
    b""  # Zero bytes = delete
)

Security consideration

Enable deletion carefully. Consider using access control to restrict who can delete files.

Organize uploads by path

Uploaded files are stored relative to upload_dir. You can organize them with paths:

# These create files in subdirectories
teyaotlani upload spartan://localhost:3000/posts/2024/entry.gmi -f entry.gmi
teyaotlani upload spartan://localhost:3000/images/photo.png -f photo.png

The server creates subdirectories automatically.

Combine with access control

Restrict who can upload files:

[upload]
enabled = true
upload_dir = "./uploads"
max_upload_size = 10485760

[access_control]
enabled = true
allow_list = ["192.168.1.0/24"]  # Only local network
default_allow = false

Verify upload success

Check the response status after uploading:

from teyaotlani import SpartanClient, is_success, is_error

async def upload_with_verification():
    async with SpartanClient() as client:
        response = await client.upload(
            "spartan://localhost:3000/file.txt",
            b"content"
        )

        if is_success(response.status):
            print(f"Upload successful: {response.meta}")
        elif is_error(response.status):
            print(f"Upload failed: {response.meta}")

Complete upload configuration

[server]
host = "localhost"
port = 3000
document_root = "./capsule"

[upload]
enabled = true
upload_dir = "./uploads"
max_upload_size = 10485760
enable_delete = true

[rate_limit]
enabled = true
capacity = 5
refill_rate = 0.5

[access_control]
enabled = true
allow_list = ["127.0.0.1/32", "192.168.1.0/24"]
default_allow = false

Next steps