Rindle
Media, made durable.
Phoenix/Ecto-native media lifecycle library. Rindle owns the durable work that happens after upload: session tracking, verification, asset state, variants, background processing, signed delivery, and cleanup.
README.md is the narrow quickstart. guides/getting_started.md
is the canonical deep adopter guide for the same first-run path.
Install
Add Rindle to your deps:
def deps do
[
{:rindle, "~> 0.1"}
]
end
If you use the S3 adapter, also choose an ExAws HTTP client. :hackney is the
most-tested path in this repo:
def deps do
[
{:rindle, "~> 0.1"},
{:hackney, "~> 1.20"}
]
end
Run mix deps.get.
Runtime Ownership
Rindle persists through your adopter-owned Repo. Configure that explicitly:
config :rindle, :repo, MyApp.Repo
Rindle also requires the default Oban path for background work. Adopters own
the Oban supervision tree, queue config, and default Oban Repo:
config :my_app, Oban,
repo: MyApp.Repo,
queues: [
rindle_promote: 5,
rindle_process: 10,
rindle_purge: 2,
rindle_maintenance: 1
]Migrations
Run your host app migrations and the packaged Rindle migrations explicitly. The
consumer smoke lane proves this Application.app_dir/2 path from a generated
Phoenix app:
rindle_path = Application.app_dir(:rindle, "priv/repo/migrations")
host_path = Path.join([File.cwd!(), "priv", "repo", "migrations"])
{:ok, _, _} =
Ecto.Migrator.with_repo(MyApp.Repo, fn repo ->
for path <- [host_path, rindle_path] do
Ecto.Migrator.run(repo, path, :up, all: true)
end
end)
Rindle does not ship a public mix rindle.* install task for this in v1.1.
The public path is the docs snippet above; the repo-private automation lives in
the install smoke harness.
First Run: Presigned PUT
The first-run path is direct upload by presigned PUT. Multipart upload is supported, but it is an advanced capability and not the default onboarding story.
alias Rindle.Upload.Broker
{:ok, session} =
Broker.initiate_session(MyApp.MediaProfile, filename: "photo.png")
{:ok, %{session: signed, presigned: presigned}} =
Broker.sign_url(session.id)
# your client PUTs bytes to presigned.url
{:ok, %{session: completed, asset: asset}} =
Broker.verify_completion(session.id)
{:ok, signed_url} =
Rindle.Delivery.url(MyApp.MediaProfile, asset.storage_key)That is the same public path proven by the built-artifact install smoke and the canonical adopter lifecycle test.
Next Reads
guides/getting_started.md: canonical deep adopter guide for Repo ownership, Oban ownership, migrations, profile setup, and the same presigned PUT lifecycleguides/background_processing.md: default Oban ownership and queue detailsguides/storage_capabilities.md: capability boundaries, including multipart as an advanced path
GSD Hygiene
For local GSD cleanup, run mix gsd.clean. It removes known transient outputs,
prunes stale worktree metadata, and reports any remaining .planning/ dirt
without deleting tracked planning artifacts.
Use the GSD workflows for the tracked planning lifecycle:
$gsd-complete-milestonewhen a milestone is actually done$gsd-cleanupto archive completed milestone phase directories$gsd-pr-branchto prepare a review branch without.planning/commits
License
MIT