script hosting
  • Python 58%
  • HTML 27.6%
  • CSS 12.8%
  • Dockerfile 1.6%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-07-16 21:34:56 -04:00
.idea CRLF 2026-07-16 16:22:28 -04:00
app private 2026-07-16 21:34:56 -04:00
.dockerignore init 2026-07-16 15:59:21 -04:00
.env.example init 2026-07-16 15:59:21 -04:00
.gitignore init 2026-07-16 15:59:21 -04:00
Caddyfile init 2026-07-16 15:59:21 -04:00
docker-compose.yml init 2026-07-16 15:59:21 -04:00
Dockerfile init 2026-07-16 15:59:21 -04:00
README.md private 2026-07-16 21:34:56 -04:00
requirements.txt add stuff 2026-07-16 20:35:50 -04:00
wsgi.py init 2026-07-16 15:59:21 -04:00

scripty

A tiny self-hosted pastebin / link shortener for one-liner install scripts, built with Flask. Visit /<scriptName> to get the raw script (so curl -fsSL https://get.example.dev/myapp | bash just works), or open it in a browser for a syntax-friendly viewer. It also works as a link shortener / redirector with a hidden-source proxy mode. Manage everything through a login-protected admin UI.

Features

  • GET /<name> serves raw script text (curl ... | bash friendly) or an HTML viewer when opened in a browser (content-negotiated via the Accept header).
  • GET /raw/<name> always serves raw text.
  • Private scripts — a per-script checkbox hides a script from the public index while still serving it by direct URL, so a private install one-liner works but isn't listed for discovery.
  • Links with two modes, set per link in the admin UI:
    • Redirect — GET /<name> returns an HTTP 302 to the target URL (classic shortener behaviour).
    • Proxy download — GET /<name> fetches the target server-side and streams the file back to the client. The real source URL is never exposed (no Location header), so downloaders only ever see your short URL. Optional overrides for Content-Type and download filename.
  • Admin web UI (login-protected) to create, edit, and delete scripts and links.
  • SQLite storage, session-based auth, password hashing.
  • Ships as a Docker image behind Caddy (automatic HTTPS in production).

Local development

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

cp .env.example .env      # then edit SECRET_KEY and ADMIN_PASSWORD
flask --app wsgi run --debug

The SQLite database is created at instance/scripty.db on first run, and the admin account from .env is bootstrapped automatically.

Deploy with Docker + Caddy

docker-compose.yml runs the Flask app (gunicorn) behind Caddy, a lightweight reverse proxy that provisions HTTPS certificates automatically.

  1. Configure environment:

    cp .env.example .env
    # set SECRET_KEY, ADMIN_PASSWORD, and DOMAIN (e.g. get.yena.dev)
    
  2. Point your domain's DNS at the host, then:

    docker compose up -d --build
    
    • If DOMAIN is empty, Caddy serves plain HTTP on port 80 (local testing).
    • If DOMAIN is set (e.g. get.yena.dev), Caddy obtains and renews a Let's Encrypt certificate automatically.
  3. The SQLite database persists in the ./data volume mount.

Usage

Log in at /login, create a script named e.g. myapp, then distribute:

curl -fsSL https://get.example.dev/myapp | bash

For links, pick a mode when creating:

  • Redirect: https://get.example.dev/docs -> 302 to your real docs URL.
  • Proxy download: https://get.example.dev/myapp.tar.gz streams the file from a hidden upstream URL; the client only sees the short URL.

Project layout

scripty/
├── app/
│   ├── __init__.py        # app factory, db/login init, admin bootstrap
│   ├── config.py          # env-driven configuration
│   ├── models.py          # Script + Link + User models
│   ├── auth.py            # login/logout blueprint
│   ├── routes.py          # raw serving, links (redirect/proxy), admin CRUD
│   ├── templates/         # base, login, index, dashboard, forms, viewer
│   └── static/style.css
├── wsgi.py                # gunicorn entrypoint
├── requirements.txt
├── Dockerfile
├── docker-compose.yml     # app + caddy
├── Caddyfile              # reverse proxy + auto-HTTPS
└── .env.example