Personal ShareX hosting app:
- Blazor Server (.NET 9) frontend + API
- Self-hosted Convex backend for metadata and storage
- Real-time admin dashboard via Convex WebSocket subscriptions (
Convex.Client+Convex.Client.Blazor)
Fully "Vibe Engineered" repo, I have a blog post on my blog if interested in reading how this was built.
- ShareX upload endpoints:
POST /i/(image multipart)POST /f/(file multipart)POST /t/(form fieldtext)
- Public view pages:
/i/{slug},/f/{slug},/t/{slug}
- Image content endpoint (cached, ETag, range requests):
GET /content/i/{slug}
- Public download endpoints:
/download/i/{slug},/download/f/{slug},/download/t/{slug}
- Admin pages:
/admin,/admin/dashboard,/admin/images,/admin/files,/admin/texts,/admin/tools- Dashboard and per-type admin pages use real-time Convex WebSocket subscriptions (stats update live without refresh)
- ASP.NET Core built-in
AddRateLimiterwith route-aware global partitions:- Upload endpoints (per-IP)
- Public view pages (per-IP + slug)
- Download endpoints (per-IP + slug)
- Admin login (per-IP, stricter window)
- Returns
429with JSON error body on rejection.
- Configurable max upload size per type:
- Images:
MAX_IMAGE_MB(default 20) - Files:
MAX_FILE_MB(default 100) - Text:
MAX_TEXT_KB(default 1024)
- Images:
- Streamed upload/download handling to avoid large in-memory buffering.
- Existing rollback-safe cleanup flow remains in place.
ADMIN_PASSWORD_HASH(PBKDF2-SHA256 format; random temporary password if not set).SHAREX_AUTH_KEY_HASH(PBKDF2-SHA256 format; random temporary key if not set).- Startup logs warn when defaults are active and print the temporary values plus sample hashes.
- Generate hashes via
/admin/tools. - Session cookie hardening:
HttpOnlySameSite=StrictSecurein non-development
- Anti-forgery validation on
/admin/loginand/admin/logout. - Optional admin IP allowlist (
ADMIN_IP_ALLOWLIST).
TRUSTED_PROXY_IPScontrols which reverse proxies are trusted for forwarded IP headers.- If not configured in production, direct connection IP is used and a warning is logged.
- Structured logs are emitted to container
stdout/stderr(JSON console formatter). - In Dokploy, view these logs directly in the service logs panel.
CONVEX_URLCONVEX_ADMIN_KEYINTERNAL_API_SECRETPUBLIC_BASE_URL
ADMIN_PASSWORD_HASH(PBKDF2-SHA256)SHAREX_AUTH_KEY_HASH(PBKDF2-SHA256)
TRUSTED_PROXY_IPSADMIN_IP_ALLOWLISTRATE_LIMIT_UPLOAD_PER_MINUTE(default20)RATE_LIMIT_VIEW_PER_MINUTE_PER_SLUG(default60)RATE_LIMIT_DOWNLOAD_PER_MINUTE_PER_SLUG(default30)RATE_LIMIT_ADMIN_LOGIN_PER_15MIN(default5)MAX_IMAGE_MB(default20)MAX_FILE_MB(default100)MAX_TEXT_KB(default1024)
Both ADMIN_PASSWORD_HASH and SHAREX_AUTH_KEY_HASH use PBKDF2-SHA256 format:
pbkdf2$sha256$<iterations>$<saltBase64>$<hashBase64>
The easiest way to generate hashes is via the admin Tools page at /admin/tools.
Alternatively, use PowerShell:
$password = "replace-me"
$iterations = 210000
$salt = [byte[]]::new(16)
[System.Security.Cryptography.RandomNumberGenerator]::Fill($salt)
$hash = [System.Security.Cryptography.Rfc2898DeriveBytes]::Pbkdf2(
[System.Text.Encoding]::UTF8.GetBytes($password),
$salt,
$iterations,
[System.Security.Cryptography.HashAlgorithmName]::SHA256,
32
)
"pbkdf2`$sha256`$$iterations`$$( [Convert]::ToBase64String($salt) )`$$( [Convert]::ToBase64String($hash) )"dotnet restore
dotnet build
dotnet test
dotnet run --project src/UndefinedLabs/UndefinedLabs.csprojThen sign in at /admin.
GitHub Actions (.github/workflows/deploy.yml) runs on every push to master and on PRs:
- Restore, build, and test the .NET solution
- On
masterpushes only: build the Docker image and push toghcr.io/kyle-undefined/undefinedlabs.tech
The image is tagged with latest and the short commit SHA.
Dokploy pulls the latest image from GHCR for deployment.
docker build -t undefinedlabs .
docker run --env-file .env -p 8080:8080 undefinedlabsDockerfileruns the .NET app on port8080.
sharex/undefinedlabs-image.sxcusharex/undefinedlabs-file.sxcusharex/undefinedlabs-text.sxcu
Update each with:
- your domain
Authorizationbearer key (the plaintext key that was hashed intoSHAREX_AUTH_KEY_HASH)
- Enable bot mitigation (Bot Fight Mode or managed bot protections).
- Add rate-limit rules for:
POST /i/,/f/,/t/GET /download/*GET /i/*,/f/*,/t/*POST /admin/login(strictest)
App-level limits should stay enabled even with edge protections.
- Current setup is intentionally single-user local auth.
- If needed later, move admin auth to Authentik (forward-auth or OIDC) and keep app endpoints unchanged.