A lightweight, open‑source tool for secure link-based access control with built-in observability and monitoring features.
After verifying a URL "knock" on a shared link, Sneak Link issues a cookie that grants access to a protected service. No IP whitelisting required.
Supports NextCloud, Immich, Paperless-ngx, Seafile and Photoprism, with extensible architecture for additional services.
- URL‑knocking trigger to initiate and verify access
- Token stored in cookie for session validation*
- Rate limiting and security event tracking
- No IP whitelisting required
*Paperless serves the document directly on the share URL - no session is granted, direct access restricted to paths with /share/-prefix
- Real-time web dashboard with system metrics and analytics
- Prometheus metrics endpoint for monitoring integration
- Historical data tracking with SQLite persistence
- Session analytics and request logging
- Minimal dependencies and simple setup
- Docker support with pre-built images
- Extensible architecture for additional services
Sneak Link includes a simple monitoring dashboard that provides real-time insights into your system's performance, security events, and user activity.
Dashboard features:
- Real-time system metrics
- Active session tracking with geolocation data
- Dark/light mode support for comfortable viewing
Prometheus integration:
- Standard Prometheus metrics format at
/metricsendpoint - HTTP request metrics (count, duration, status codes)
- Security and rate limiting metrics
- Service-specific validation tracking
- System uptime and performance monitoring
- Ready for Grafana dashboards and alerting
You want to share content from your self-hosted services (photos, documents, files) with external users, family, friends, or clients, without requiring them to set up VPNs or exposing your services directly to the internet.
Local network:
- eg.
nextcloud.yourdomain.comresolves to internal services via local DNS - your self-hosted service responds to web traffic to that domain name
Public internet:
- eg.
nextcloud.yourdomain.comresolves to your public IP - sneak-link responds to web traffic to that domain name
- sneak-link is configured to proxy traffic to your private services based on hostname using local DNS
-
Create share links: You create share links in your services for content you want to share:
- NextCloud:
/s/AbCdEf123(shared folder or file) - Immich:
/share/XyZ789(photo album) - Paperless-ngx:
/share/secret123(specific document) - Seafile
"/f/AbCdEf123", "/d/AbCdEf123", "/u/d/AbCdEf123" - Photoprism:
/s/k2yta5ims0(photo collection)
- NextCloud:
-
URL knocking: You send the complete URL to someone who needs access:
https://nextcloud.yourdomain.com/s/AbCdEf123https://immich.yourdomain.com/share/XyZ789https://paperless.yourdomain.com/share/secret123https://photoprism.yourdomain.com/s/k2yta5ims0
-
Validation: When recipients click the link:
- sneak-link receives the request and identifies the service by hostname
- Validates the share exists by checking if your private service returns HTTP status 200
- If the share doesn't exist (HTTP status 404 or 401), access is denied
- Rate limiting prevents brute force attempts on share URLs
-
Access granted: For valid shares:
- NextCloud/Immich/Seafile/Photoprism: sneak-link issues a service-specific cookie for full app access
- Paperless-ngx: Direct proxy without cookies (single-request access only)
- User is transparently proxied to your service instance
- No IP whitelisting: External users don't need static IPs or VPN access
- Valid shares only: Only existing share links grant access
- Rate limiting: Prevents brute force attacks on share URLs
- Session management: Cookie-based access with configurable expiration
- Private network: NextCloud and Immich remains on private network, not directly exposed
This approach enables secure external sharing from your self-hosted services without compromising security or requiring technical expertise from recipients.
- NextCloud, Immich, Paperless, Seafile, and/or Photoprism instance running on your private network
- Domain name with split-brain DNS control
- Docker installed
-
Generate a secure signing key:
SIGNING_KEY=$(pwgen -n 32 1) -
Run sneak-link using the pre-built image:
docker run -d \ --name sneak-link \ -p 8080:8080 \ -p 9090:9090 \ -p 3000:3000 \ -v sneak-link-data:/data \ -e NEXTCLOUD_URL=https://nextcloud.yourdomain.com \ -e IMMICH_URL=https://immich.yourdomain.com \ -e PAPERLESS_URL=https://paperless.yourdomain.com \ -e SEAFILE_URL=https://seafile.yourdomain.com \ -e PHOTOPRISM_URL=https://photoprism.yourdomain.com \ -e SIGNING_KEY=$SIGNING_KEY \ ghcr.io/felixandersen/sneak-link:latest -
Configure your reverse proxy to forward public HTTPS traffic to port 8080
Note: sneak-link runs the proxy service on HTTP internally (port 8080). A reverse proxy (nginx, Caddy, Traefik, etc.) must handle HTTPS termination and forward HTTP traffic to sneak-link.
That's it!
| Variable | Required | Default | Description |
|---|---|---|---|
NEXTCLOUD_URL |
No* | - | NextCloud instance URL |
IMMICH_URL |
No* | - | Immich instance URL |
SEAFILE_URL |
No* | - | Seafile instance URL |
PAPERLESS_URL |
No* | - | Paperless-ngx instance URL |
PHOTOPRISM_URL |
No* | - | Photoprism instance URL |
SIGNING_KEY |
Yes | - | Secret key for signing authentication tokens |
LISTEN_PORT |
No | 8080 | Port for the HTTP server |
COOKIE_MAX_AGE |
No | 86400 | Cookie expiration time in seconds |
RATE_LIMIT_REQUESTS |
No | 10 | Maximum requests per IP per window |
RATE_LIMIT_WINDOW |
No | 300 | Rate limiting window in seconds |
LOG_LEVEL |
No | info | Log level (debug, info, warn, error) |
METRICS_PORT |
No | 9090 | Port for Prometheus metrics endpoint |
DASHBOARD_PORT |
No | 3000 | Port for web dashboard |
DB_PATH |
No | /data/sneak-link.db | SQLite database path for metrics storage |
METRICS_RETENTION_DAYS |
No | 30 | Data retention period in days |
*At least one service URL must be configured
- Dashboard:
http://your-host:3000/- Web interface for monitoring and analytics - Metrics:
http://your-host:9090/metrics- Prometheus-compatible metrics endpoint - Health Check:
http://your-host:9090/health- Service health status
The SQLite database stores historical data at the configured DB_PATH and can be mounted as a volume in Docker for persistence.
- Share URL Security: Relies on your self-hosted service generating cryptographically secure random share URLs. Weak entropy or other vulnerabilities in the share URLs compromises the security model.
- Rate Limiting: IP-based rate limiting can be bypassed with distributed attacks. Consider additional protection at the reverse proxy level.
- Session Management: Cookies persist until expiration even if the original NextCloud or Immich share is deleted. No automatic session invalidation.
- Session Isolation: After accessing a valid share link, users gain full access to the service's login portal and can potentially access other content. Sneak Link does not implement service-specific session isolation or access controls beyond the initial share validation.
- Cookie Compliance: Uses cookies for authentication. Consider privacy laws (GDPR, etc.) if deploying for business use or public access.
- Logging Privacy: Access logs contain IP addresses and usage patterns. Implement appropriate log retention and privacy policies.
sneak-link outputs structured JSON logs with the following event types:
- access: HTTP request logs with IP, method, path, status, duration
- security: Security events like rate limiting, invalid tokens, unauthorized access
- validation: Share validation attempts with results
Example log output:
{"level":"info","msg":"HTTP request","time":"2024-01-01T12:00:00Z","type":"access","ip":"1.2.3.4","method":"GET","path":"/s/AbCdEf123","status":200,"duration":45}
{"level":"warn","msg":"Security event","time":"2024-01-01T12:00:01Z","type":"security","event":"rate_limit_exceeded","ip":"1.2.3.4","details":"requests: 11, window: 5m0s"}