# Deploy expiring text pastes with Sinatra on Yeeted

A Yeeted sample on the **rack/sinatra path** (a Gemfile with no rails). Text
pastes with a TTL.

- **Managed postgres, self-migrating** at startup (`migrations/*.sql` tracked
  in `schema_migrations`).
- **TTL enforced at read time** - an expired paste is a 404 the moment its
  clock runs out, and the store exposes a sweep method for cleanup.
- **Memory-store fallback** keeps the app and its tests service-free.

Tests: `ruby test/test_app.rb` (unit) ·
`PASTEBIN_URL=https://... test/integration.sh` · `./smoke.sh https://...`.

## Run locally

Use Ruby 3.1 or newer, Bundler, and the PostgreSQL client development libraries
needed to install the `pg` gem. From `ruby/pastebin-sinatra`:

```sh
bundle install
bundle exec ruby test/test_app.rb
bundle exec rackup -o 0.0.0.0 -p 3000
```

Open `http://localhost:3000`. An unset `DATABASE_URL` selects the memory store.
A configured PostgreSQL URL selects the persistent store and its migrations.

## Deploy and verify

Follow the [deployment walkthrough](getting-started.md). Deploy this
folder with its `Gemfile`, `config.ru`, migrations, and `yeeted.yaml`. Yeeted
uses the Rack/Sinatra path and attaches the required PostgreSQL database.

```sh
sh smoke.sh "$PREVIEW_URL"
PASTEBIN_URL="$PREVIEW_URL" sh test/integration.sh
curl -i "$PREVIEW_URL/api/pastes?ttl=60"   -H 'Content-Type: text/plain' --data-binary 'Hello from Yeeted'
```

Expect 201 with a `slug`, a relative `url`, and `expires_in`. Fetch that URL
before expiry to read the text; after expiry it returns 404. Always send
`Content-Type: text/plain`: curl's default form encoding is rejected with 415.
The default TTL is one hour, the maximum is seven days, and text is limited
to 64 KiB per paste.

## What to change before real use

Expiry hides a paste from reads. The store provides `sweep`, but this example
does not schedule a background cleanup task, so expired database rows remain
until cleanup is run. There is no access control: anyone with a paste URL can
read it before expiry. Add cleanup scheduling and an access policy if needed,
and coordinate migrations before running multiple replicas.

[All examples](index.md) · [Deploy an example](getting-started.md)
