Static Files Hosting
Host static content on *.{domain} subdomains with automatic SSL.
Note: The domain is configured via
SF_DOMAINenvironment variable (set during installation). Examples below useyourdomain.comas placeholder.
Preflight (Required)
Run this before any sf ... command:
# 1) Required env
API_URL="${SF_API_URL:-http://localhost:3000}"
echo "$API_URL"
echo "$SF_API_KEY"
# 2) API health
curl -i "$API_URL/health"
# 3) Doctor (if available in your sf version)
if sf help 2>/dev/null | grep -q "doctor"; then
sf doctor
fi
Expected:
- API URL resolves to
http://localhost:3000unlessSF_API_URLis set SF_API_KEYis set/healthreturns200- If available,
sf doctorshowshealthandauthchecks as PASS
If preflight fails, fix runtime/config first; do not proceed with uploads/site changes.
Runtime Mode Detection
Choose the branch that matches your environment:
A) Systemd Host (recommended production mode)
sudo /opt/kleo-static-files/install.sh --status
systemctl status kleo-static-files
Use this when systemd is available and managing the service.
B) No-Systemd Session / Container / CI
cd /opt/kleo-static-files
set -a && source .env && set +a
bun run server/index.ts
In no-systemd environments, run the API process manually and keep it alive in your session/supervisor.
Quick Reference
# Create site
sf sites create mysite
# → https://mysite.yourdomain.com (or your configured domain)
# Upload file
sf upload ./index.html mysite
# Upload directory
sf upload ./dist mysite
# Add authentication
sf sites auth mysite admin:secretpass123
# List files
sf files mysite
# Delete file
sf files mysite delete path/to/file.txt
# Delete site
sf sites delete mysite
Environment Setup
export SF_API_URL=http://localhost:3000 # API endpoint
export SF_API_KEY=sk_xxxxx # Your API key
export SF_DOMAIN=yourdomain.com # Your configured domain (optional, for URL display)
Workflows
Deploy a Static Website
# 1. Create the site
sf sites create docs
# 2. Upload the build directory
sf upload ./build docs
# 3. Verify (replace with your domain)
curl -I https://docs.yourdomain.com
Protected File Sharing
# 1. Create site with auth
sf sites create private
sf sites auth private user:strongpassword
# 2. Upload sensitive files
sf upload ./reports private
# 3. Share URL + credentials
# https://private.yourdomain.com (user / strongpassword)
Update Existing Files
# Overwrite existing file
sf upload ./new-version.pdf mysite --overwrite
# Or delete and re-upload
sf files mysite delete old-file.pdf
sf upload ./new-file.pdf mysite
CLI Commands
sites
| Command | Description |
|---|---|
sf sites list |
List all sites |
sf sites create <name> |
Create new site |
sf sites delete <name> |
Delete site and all files |
sf sites auth <name> <user:pass> |
Set basic auth |
sf sites auth <name> --remove |
Remove auth |
upload
sf upload <path> <site> [subdir] [--overwrite] [--json]
path: File or directory to uploadsite: Target site namesubdir: Optional subdirectory--overwrite: Replace existing files--json: Output JSON
files
| Command | Description |
|---|---|
sf files <site> |
List all files |
sf files <site> delete <path> |
Delete specific file |
stats
sf stats # Global stats
sf stats <site> # Site-specific stats
API Endpoints
Base: $SF_API_URL with Authorization: Bearer $SF_API_KEY
| Method | Path | Description |
|---|---|---|
| GET | /sites |
List sites |
| POST | /sites |
Create site |
| DELETE | /sites/{name} |
Delete site |
| PATCH | /sites/{name} |
Update auth |
| GET | /sites/{name}/files |
List files |
| POST | /sites/{name}/files |
Upload file |
| DELETE | /sites/{name}/files/{path} |
Delete file |
| GET | /stats |
Global stats |
| GET | /stats/{name} |
Site stats |
| GET | /health |
Health check |
Constraints
- Site names: lowercase, alphanumeric, hyphens only (max 63 chars)
- File size: 50MB default (configurable)
- Quota: 100MB per site default
- Rate limit: 100 requests/minute per API key
Troubleshooting
"Cannot connect to API"
# Fast diagnosis
sf doctor
# Manual health check
curl -i "$SF_API_URL/health"
If systemd host:
sudo /opt/kleo-static-files/install.sh --status
systemctl status kleo-static-files
journalctl -u kleo-static-files -n 100 --no-pager
If no-systemd runtime:
pgrep -af "bun run server/index.ts"
cd /opt/kleo-static-files
set -a && source .env && set +a
bun run server/index.ts
# In another shell
curl -i "$SF_API_URL/health"
"Invalid API key"
# Verify key is set
echo $SF_API_KEY
# Validate auth path
sf doctor --json
# Create new key if needed
bun run /opt/kleo-static-files/scripts/create-key.ts "new-key"
"Quota exceeded"
# Check current usage
sf stats mysite
# Delete unused files
sf files mysite delete large-file.zip
Site not accessible via HTTPS
# Verify DNS points to server (use your domain)
dig mysite.yourdomain.com
# Check Caddy config
cat /etc/caddy/sites.d/static-files.caddy
# Resync Caddy
bun run /opt/kleo-static-files/scripts/sync-caddy.ts --reload
Installation
Server Installation (run once on host)
curl -fsSL https://raw.githubusercontent.com/498AS/kleo-static-files/main/install.sh | sudo bash
The installer outputs JSON with connection details between KLEO_SF_CONFIG_BEGIN and KLEO_SF_CONFIG_END markers. Parse this to get api_url and api_key.
After Installation
export SF_API_URL=http://localhost:3000
export SF_API_KEY=sk_xxxxx # from installer output
Check Status
sudo /opt/kleo-static-files/install.sh --status
See references/install.md for manual installation.
Alternative CLI (Shell Wrapper)
If you need a lightweight alternative to the Bun CLI, use the curl-based shell wrapper:
# Use directly
/opt/kleo-static-files/cli/sf.sh sites list
# Or create an alias
alias sf='/opt/kleo-static-files/cli/sf.sh'
# Works the same way
sf sites create mysite
sf upload ./files mysite
The shell wrapper requires only bash and curl, no Bun installation needed.