Build FileChanger

YESreplaces $39/mosaves $468/yrback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A personal conversion API whose hard problems are not the conversions, which other people's software does, but discovering what the installed engines can actually do, fencing each invocation so a bad file cannot take the machine with it, and reporting failures in a way that is useful to you and useless to an attacker.

estimated effort weekendthe files for this build are in the project pack

RuntimePython with a small HTTP framework, one process, no brokerEnginesPandoc for markup, an office suite in headless mode, ImageMagick for images, FFmpeg for mediaStateSQLite for jobs and keys, files on disk in per-job directoriesDiscoveryThe format matrix built at startup by asking each binary what it supportsIsolationA temporary directory, a wall-clock timeout, an output cap and no network per conversion

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the service, the worker and the MCP server.

    Get it python.org, or your version manager. open ↗

    Verify python3 --version prints 3.12 or higher

  2. installfree

    Why These four do all the work, and the format list you will advertise is exactly what they report.

    Get it Your package manager, or the container image this build ships.

    Verify Each binary prints a version and the startup probe lists its formats

  3. installfree, but the install is large

    Why Pandoc does not produce PDF on its own; it hands off to a typesetting engine, and forgetting this is the most common first failure in a converter.

    Get it Install a supported typesetting engine and name it in the configuration.

    Verify A markup file converts to a PDF that opens

  4. decidefree

    Why The distribution default may already disable the delegates you need, or may not disable ones you would rather not run, and either way it is not your decision until you commit one.

    Get it Read the image engine's own security policy documentation and write a policy covering resource limits and which coders are permitted.

    Verify The policy file is committed and the service starts with it

  5. installfree

    Why Pinning every engine and its fonts in one image is what makes an upgrade a deliberate act rather than a surprise.

    Get it Any container runtime with compose support.

    Verify The image builds and the engine round trips pass inside it

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- engines(name, binary_path, version_string, probed_at) · the version is what explains a conversion that worked last month
- formats(engine, direction, format_name, probed_at) · direction is input or output, discovered rather than typed
- jobs(id, created_at, source_format, target_format, input_bytes, state, started_at, finished_at, exit_code, failure_kind, output_bytes, output_path)
- job_logs(job_id, stream, content) · the command and its error output, stored and never returned over HTTP
- api_keys(id, name, hash, created_at, last_used_at, revoked_at) · the plaintext is shown once and never stored
- limits(name, value) · timeout seconds, maximum input bytes, maximum output bytes, per-key concurrency
- The format matrix is a cache of what the installed binaries said, with the date they said it. An upgrade invalidates it, which is the point.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DATABASE_PATHrequired./data/jobs.dbJobs, logs, keys and the format matrix cache.
WORK_DIRrequired./data/workPer-job directories. Deleted on completion and on retention.
INLINE_MAX_BYTESrequired8388608Above this a conversion becomes a job instead of a response.
CONVERT_TIMEOUT_SECONDSrequired120Wall clock per conversion. A timeout is a distinct reason class.
OUTPUT_MAX_BYTESrequired536870912A small input can expand enormously, deliberately or otherwise.
JOB_RETENTION_HOURSrequired24Job directories are removed after this whether collected or not.
IMAGEMAGICK_POLICY_PATHrequired./config/imagemagick-policy.xmlThe committed policy, used instead of whatever the distribution shipped.
WORKER_COUNTrequired2Each worker gets its own office-suite profile directory derived from its index.

The build, in order

  1. One engine, one round trip, one honest failure

    A single conversion works end to end and a failed conversion is reported as failed with a reason class.

    1. Error output is a description of your filesystem.

      Files src/convert/run.pytests/test_no_leak.py

    2. The failure path is where the leftovers come from.

      Files src/convert/workspace.py

    3. They have completely different fixes.

      Files src/convert/classify.py

    done when · tick each as it passes
    watch out
    • Returning the engine's error output to the caller is the natural debugging shortcut and it hands over absolute paths, installed versions and sometimes fragments of the document itself. Store the command and its error in the job log, return a reason class and a job id, and make the only path to the detail a local command you run yourself.
  2. Ask the binaries what they can do

    The supported format matrix comes from the installed engines at startup, with their versions recorded.

    1. The version is the first thing you will want after a surprise.

      Files src/engines/probe.pytests/test_probe.py

    2. Failing after a large upload wastes everybody's time.

      Files src/api/convert.py

    3. A cached answer should say how old it is.

      Files src/api/formats.py

    done when · tick each as it passes
    watch out
    • Hard-coding a list of a few hundred format pairs produces a service that accepts a conversion the installed binary can no longer do, wastes the upload and returns an engine error that looks like a bug in your code. Ask the binaries, cache what they said with the date, and treat a version change as a reason to re-probe rather than a curiosity.
  3. Fencing each conversion

    One conversion cannot exhaust the machine, reach the network, or be affected by another running at the same time.

    1. A shared profile fails only under concurrency, which is where you use it.

      Files src/engines/office.pytests/test_concurrent_office.py

    2. The distribution default is a decision somebody else made.

      Files config/imagemagick-policy.xml

    3. A small input can produce an enormous output on purpose.

      Files src/convert/limits.py

    done when · tick each as it passes
    watch out
    • Running two office-suite conversions at once with the default shared profile produces a job that exits without an output file and without a useful error, and because it only happens under concurrency it survives every single-file test you write. Give each worker its own profile directory derived from the worker index, and add a concurrency test that runs several conversions of the same type simultaneously.
  4. Big files become jobs

    A conversion too large to answer inline becomes a job that survives a restart.

    1. One route, two shapes, is easier to use than two routes.

      Files src/api/convert.pytests/test_threshold.py

    2. A partial output is not a checkpoint.

      Files src/jobs/recover.py

    3. Video is the reason this matters.

      Files src/api/result.py

    done when · tick each as it passes
    watch out
    • Marking an interrupted job as queued again on restart re-runs a conversion whose partial output is still on disk, and the second attempt writes over it while a client is streaming the first, which produces a corrupt download nobody can reproduce. Mark it interrupted, delete the partial output, and let the caller decide whether to ask again.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more documents & pdfs.