Skip to content

Releases: AlyShmahell/MatchMedia

v0.0.9

Choose a tag to compare

@AlyShmahell AlyShmahell released this 25 Sep 19:11

Changelog

MatchMedia v0.0.9

  • Implemented the XDG Base Directory Specification, relative to current user's $HOME
  • Implemented an XDG enabled and config-driven browse_root, limited to $XDG_VIDEOS_DIR, $XDG_MUSIC_DIR, /mnt, /media
  • Moved the dev runtime to a container
  • Limited the default http addr to localhost

v0.0.8

Choose a tag to compare

@AlyShmahell AlyShmahell released this 19 Sep 00:09

Changelog

MatchMedia v0.0.8

  • POST /v1/scan takes mode: omit or "rescan" is the 0.0.7 full match; "changes" groups the current tree and only rematches incomplete titles. 202 is {"session","files","mode"}. Unknown mode or a non-empty path that is not absolute under browse_root is 400.
  • Changes completeness is title identity: tvshow.nfo plus a show poster (poster.jpg / folder.jpg) or a season-folder poster. Movies still need that item’s NFO + poster. Episode stills (-thumb.jpg) never rematch. uniqueid is optional (empty provider/id is fine).
  • require_episode_nfo is Changes-only (*bool, default true): every owned video (including Specials/extras on the parent) needs episode.nfo or <basename>.nfo. "false" skips that. Rescan ignores the field.
  • Complete Changes titles leave grouping as status: matched, one Score 1.0 candidate from the sidecar, catalog: [], and never enter the provider worker. Gone or renamed-away paths are omitted; a rename without sidecar is pending at the new path.
  • Ingest, ranker, POST /v1/match, POST /v1/retry, select, and catalog HTTP are unchanged.

v0.0.7

Choose a tag to compare

@AlyShmahell AlyShmahell released this 18 Sep 12:09

Changelog

MatchMedia v0.0.7

  • match.prefer.anime is a typed-job auto-match hint only. Untyped scan/ingest never prefer, and job.candidates is always the full ranked list.
  • Displayed Score is scaled into [0, 1]: (J + title_lift · max(func, parentCov) + exact_lift · exact) / (1 + title_lift + exact_lift). Title-in-synopsis no longer fills Score to 1.0.
  • Auto-match adds a Score-leader path and a unique full-query-coverage path (nicknames and long official titles). solo_min_score is ignored.
  • Alias GETs run when any job title token is missing from the primary name (Spy×Family loads Spy x Family).
  • When Jaccard is already 1, synopsis/parent lift is omitted so identical titles are not reordered by a blurb ([Oshi no Ko] anime vs live action).
  • HTTP routes ServeMedia pins (POST /v1/scan, untyped POST /v1/ingest, poll GET /v1/jobs?session=) and skipDefer (Jaccard) are unchanged.
  • The release tarball is still binary, config/, public/, and LICENSE.

Prefer

  • prefer.anime (language: Japanese, kind: Animation) runs only when job.type is a key under match.prefer (ingest type: anime).
  • Rank always scores every provider hit. Prefer never deletes rows, so select can still pick a Scripted TVMaze title next to a Japanese Animation hit (The 100 vs The 100 Girlfriends).
  • Jikan still ships static attrs: { language: Japanese, kind: Animation } so typed anime prefer can see those hits.
  • A single prefer-shaped hit does not auto-match.

Ranking

Score is no longer J + (1 − J) · plot. After the same titleNorm (lowercase, strip non-alnum, no stemming):

  • Jaccard J is still the best |q ∩ t| / |q ∪ t| over the primary title and each title* attr.
  • Title-in-synopsis func is coverage of job title content tokens in the candidate synopsis (not plot-to-plot). Shared tokens in this result list are down-weighted (0.25).
  • exact is 1 when titleNorm of the job equals titleNorm of the primary title (aliases do not count).
  • Shipped title_lift / exact_lift are 0.15 / 0.25. Divide by 1 + title_lift + exact_lift so Score stays in [0, 1] without clamping.
  • When J == 1, func and parentCov are not added. Primary exact is (1 + exact_lift) / den (~0.89), not 1.0 from a synopsis token.

Sort is unchanged: content-token overlap, then Score, then title prefix, then Jaccard, then year.

Auto-match

job.candidates stays the full list. Auto-matched if:

  1. best max(Jaccard, Score) >= match.min_score and (one candidate, or that strength gap to second is >= match.min_margin), or
  2. best Score >= match.min_score and the Score gap to second is >= match.min_margin, or
  3. typed prefer, at least two prefer hits, best of those has queryCov == 1, and the Score gap to the next prefer hit is >= match.min_margin, or
  4. the job has a content token and exactly one work has queryCov == 1 and that row is ranked first.

Path 4 is for short nicknames (Anohana) and solo official titles with extra words (Apothecary Diaries, Berserk Memorial Edition). Provider clones that share the covering title (best-Jaccard primary/alias) and year count as one work (TVMaze + Jikan Banished). Season 2 or The Office US/UK stay manual. Girls vs Girls und Panzer still two works.

defer: true providers still run only if the fast pass is thin (fewer than match.min_hits or best Jaccard below match.min_score). A Jaccard auto-match on the fast pass skips defer. Unique queryCov is applied on the final ranked list so a weak TVMaze substring does not lock in before Jikan.

Aliases

  • Skip the extra titles GET only when the primary title already covers every job title token, not only content tokens (plot_stop / length < 3).
  • Stored aliases still must share a content token. Bookworm / Erased subset skips are unchanged.
  • Spy X Family vs TVMaze Spy×Family now fetches AKA Spy x Family, so Jaccard can be 1 via the alias.

Config

  • version: is 0.0.7.
  • New tunables: match.title_lift (shipped 0.15), match.exact_lift (shipped 0.25). Unset still defaults to those values.
  • match.solo_min_score is removed from shipped YAML and ignored if an overlay still has it.
  • Leave match.min_score / min_margin / match.workers, cooldown YAML, and provider defer / min_interval_ms / provider_timeout_ms as shipped in 0.0.6.

Upgrade notes

  • HTTP contract is unchanged. Rematch existing sessions if you want the new Score, prefer-not-filtering, and unique queryCov on old jobs (ranker: set is the same key; Score numbers are not).
  • Overlay data/config.yaml must not rely on prefer deleting non-anime rows, or on Score 1.0 from title-in-synopsis. Drop solo_min_score if present.
  • Scan jobs still have empty type. Western TV + anime-shaped hits stay on the same picker (The 100). Typed CSV type: anime still uses prefer for auto-match only.
  • Exact primary titles display ~0.89, not 1.0. Auto-match still uses max(Jaccard, Score), so Jaccard 1 still matches.
  • Unique queryCov can auto-match a unique official title below min_score. Two seasons or two years of the same covering title stay manual.

v0.0.6

Choose a tag to compare

@AlyShmahell AlyShmahell released this 15 Sep 23:29

Changelog

MatchMedia v0.0.6

  • Scan jobs carry numbered files[].
  • Immediate group.kinds / group.extras folders stay on the parent show (season 0) instead of minting a second title.
  • A cooldown or timeout on one provider no longer zeros hits from another; all-cooldown with zero candidates stays status: error.
  • HTTP routes ServeMedia pins (POST /v1/scan, untyped POST /v1/ingest, poll GET /v1/jobs?session=) and skipDefer are unchanged.
  • The release tarball is still binary, config/, public/, and LICENSE.

Scan files

  • Every scan job now has files[]: video files only (group.video_ext), never a directory, under that job’s path.
  • Longest-prefix ownership so a named sibling keeps its own tree (Show/Spin Off/ does not leak onto Show/).

Numbers when the tree implies them:

  • season-shaped folder → that number (Season 01 → 1)
  • folder classified kind or extras (YAML lists) → season 0
  • filename sxxexx / episode N (same patterns grouping already used) → those numbers; filename wins over folder
  • remaining files in a known season, sorted by path → sequential unused episode numbers

  • Loose files with no season folder and no episode pattern still appear as {path} with season/episode omitted (MapFilesOntoCatalog already skips those).
  • Catalog GET / select / worker backfill still call MapFilesOntoCatalog as before.

Kinds and extras

  • Immediate children classified kind or extras no longer emit inner scan jobs.
  • The parent is treated as a series; those videos land on it via files[] with season 0.
  • Named siblings stay a separate title with parent set as today.

  • Title clustering still uses keepVideo; file attachment uses collectVideos.
  • Word lists stay in YAML (group.kinds / group.extras); Go does not special-case folder names.

Cooldown

  • A cooldown skip does not wipe candidates from other providers.
  • If any wanted provider returned hits, the job is ranked (matched or manual) and error stays empty — even if a deferred provider is cooling or timed out.

  • Zero candidates with ok == 0 and at least one provider error (including all-cooldown) stay status: error.
  • job.Error is key: message joined by "; " (YAML provider keys).
  • Empty 200s still increment ok and become unmatched when there are no rows.
  • No new status. No worker auto-retry.

  • defer: true providers still run only if the fast pass is thin (fewer than match.min_hits or best Jaccard below match.min_score).
  • A high-Jaccard manual fast pass still skips defer.

Config

  • version: is 0.0.6.
  • No new tunables.
  • Leave match.min_score / min_margin / match.workers, cooldown YAML, and provider defer / min_interval_ms / provider_timeout_ms as shipped in 0.0.5.

Upgrade notes

  • HTTP contract is unchanged. Rematch is not required for ranking; Score / ranker: set are the same as 0.0.5.
  • Scan jobs now populate files[]. ServeMedia 0.0.3 may double-count if it still walks job.path as well as files[]. Prefer files[] when present.
  • Specials/ / OVA-style group.kinds folders and extras no longer mint a second job of the same show. Those videos are season 0 on the parent. Named spin-off folders are still separate titles.
  • Mixed “one provider hit, another cooling” is no longer error. All-cooldown with zero candidates stays a red error badge (POST /v1/retry still rematches error and unmatched).
  • CSV / JSON ingest is unchanged except for that cooldown ranking (ingest does not go through the grouper; files[] stays empty unless the client sent season/episode columns onto job.season / job.episode).

v0.0.5

Choose a tag to compare

@AlyShmahell AlyShmahell released this 03 Sep 19:48

Changelog

MatchMedia v0.0.5

SequenceMatcher stays on disk grouping only. Candidate ranking is token-set Jaccard plus a residual plot/parent score. Jobs record ranker: set. The release tarball is still binary, config/, public/, and LICENSE.

Ranking

Score is no longer a SequenceMatcher ratio (and no longer +0.15 for year). After the same titleNorm used for grouping (lowercase, strip non-alnum, no stemming):

  • Jaccard J is the best |q ∩ t| / |q ∪ t| over the job title and each candidate title string (primary title plus every attr whose key starts with title). Parent is not in J.
  • Plot func is length-weighted coverage of content tokens from the job title in the synopsis. Tokens in match.plot_stop or shorter than 3 are dropped. A token that appears in two or more synopses in this result list is down-weighted (0.25).
  • parentCov is coverage of those same content tokens from a different parent folder in the candidate title set.
  • Displayed Score = J + (1 − J) · max(func, parentCov). Exact title (J = 1) is always Score 1.
  • Year is a sort key only when the job has a year.

Sort: content-token overlap with the job title beats none; then Score; then job title tokens as a prefix of the candidate primary title; then Jaccard; then year.

That stops short near-misses (Frieden, Crashed, Arifureta Kiseki) from beating the long official title, keeps Wednesday / Limitless above suffixed shows, and lets plot lift English disk titles that only have a Japanese TVMaze show.name (Explosion → Bakuen when aliases are absent). Parent residual lifts Magi/Sinbad and Scarlet Bond without giving Adventures a free hit on the / of.

Aliases

Optional provider titles block. TVMaze ships GET /shows/{id}/akas with max: 3. After search, candidates whose primary title already covers every content token of the job skip the extra GET. At most max of the rest are fetched, paced with min_interval_ms. Failed GETs do not fail the search. Only aliases that share a content token with the job are stored as title_aka / title_aka_2 / … so an English AKA can be Jaccard 1 (Erased).

Two franchise AKAs that both copy the same English name still tie at Score 1 and stay manual (I Shall Survive Using Potions! vs Potions Will Save Me; KonoSuba main vs Explosion spin-off when TVMaze lists the spin-off title on both shows).

Auto-match and defer

match.prefer.anime (language: Japanese, kind: Animation) runs before rank. Typed anime jobs use it as before. Untyped scan jobs use the same filter when any candidate matches.

Auto-matched if:

  1. best Jaccard >= match.min_score and the Jaccard gap is >= match.min_margin, or
  2. prefer actually filtered, and either a single remaining candidate’s Score meets solo_min_score (shipped 0.01), or queryCov == 1 with the usual margin on Score.

A weak solo hit does not use solo_min_score unless prefer filtered. defer: true providers still run only if the fast pass is thin (fewer than match.min_hits or best Jaccard below min_score). A prefer-filtered auto-match on the fast pass skips Jikan.

Config

version: is 0.0.5. New / now-used tunables in config/default.yaml:

  • match.plot_stop — stop list for plot, parent residual, and content overlap
  • match.synopsis_limit — search/detail/catalog clip (shipped 4000 runes; was 400)
  • match.solo_min_score — prefer-solo floor
  • match.wait_cap — in-memory wait log cap (500)
  • scan.sample_videos — videos sampled per child (5)
  • session.ttl_max_ms — clamp on session.ttl_ms (shipped 86400000)

match.weights is gone. Score is the convex residual above, not a weighted sum.

Upgrade notes

  • Rematch existing sessions if you want set ranking, aliases, and the new Score on old jobs (ranker: seq rows stay as stored).
  • Overlay data/config.yaml must not rely on match.weights.
  • Specials/ is still a group.kinds folder, not extras. A Specials tree whose files repeat the series title can emit a second job of the same show (Arifureta). Add special / specials to group.extras if you want those folded into the parent.
  • Scan jobs still have empty type. Movies without OMDb/TMDB keys stay unmatched.

v0.0.4

Choose a tag to compare

@AlyShmahell AlyShmahell released this 31 Aug 12:41

Changelog

Matchora 0.0.4

  • No local LLM, embedder, or llama.cpp. 🎉
  • Scan grouping and candidate ranking are SequenceMatcher plus YAML.
  • The release tarball is binary, config/, public/, and LICENSE.

Scan grouping

Scan no longer calls an instruct model (prompt.md is gone). Each library child is classified from disk: seasons and extras fold into the walk-root title; named sibling folders become their own titles; leftover loose videos cluster with SequenceMatcher.

Word lists live under group: in config/default.yaml (video_ext, extras, release, kinds, seq_threshold). Years are taken only from a (YYYY) suffix or a trailing year token already on the name.

Ranking

Jobs record ranker: seq. Score is SequenceMatcher on normalized titles only:

  • Exact title match is 1.0.
  • Otherwise title ratio, with a second try against title + year (helps Girls (2012) vs Girls).
  • Matching year still adds 0.15.
  • Synopsis is not used. A one-word query whose word appeared in a synopsis no longer scores 1.0 (Wednesday vs Wednesday Club, Limitless vs Limitless with Chris Hemsworth, and similar).

True same-name collisions with no job year (Dark Matter 2015 vs 2024) stay manual.

Ingest

Unknown CSV headers are mapped with ingest.aliases, then SequenceMatcher against canonical fields when the ratio is at least group.seq_threshold. The instruct ingest.md path is gone.

Config

version: in config/default.yaml is the only version string (0.0.4). There is no root VERSION file and no compile-time version.

Grouping lists and match/http numbers come only from YAML. Missing or invalid required keys fail start. POST /v1/config that would clear a required list is 400 and is not written.

Removed: llama: block, ranker: embed, GGUF URLs, --prepare, and the llama admin form.

Packaging and docs

  • One archive: matchora-<version>-linux-amd64.tar.gz (no -llama tarball, no vendored llama.cpp).
  • Packaging fails if version is missing from the shipped YAML.
  • README is written for the release tarball: unpack, ./matchora, listen on http.addr.

Upgrade notes

  • Delete any leftover vendor/llama.cpp tree; it is unused.
  • Overlay data/config.yaml must not rely on llama: keys.
  • Rematch existing sessions if you want title-only ranking on old jobs.
  • Scan jobs still have empty type. Jikan (types: [anime]) does not run on untyped scan unless the TVMaze pass is empty. English folder titles that TVMaze stores under a Japanese name (e.g. Erased → Boku Dake ga Inai Machi) can sit below other exact-title hits; the admin UI shows the top 5 candidates.

v0.0.3

v0.0.3 Pre-release
Pre-release

Choose a tag to compare

@AlyShmahell AlyShmahell released this 29 Aug 13:17

Changelog

0.0.3 - 2026-08-29

Scan/ingest are isolated per request. Title-finding is back to the August 17 loop (one child, one instruct call, match kicked immediately), plus a title folder path.

Breaking

  • POST /v1/scan returns 202 {"session","files"}. POST /v1/ingest returns 202 {"session","jobs"} (jobs are no longer a bare array).
  • GET/DELETE /v1/jobs, GET /v1/scan/status, GET /v1/match/log, POST /v1/match, POST /v1/retry, select/catalog-on-job, and catalog reads require ?session=. Missing id is 400; unknown or expired is 404.
  • Jobs persist as {data_dir}/jobs-{session}.json, not jobs.json.
  • Scan no longer attaches video files to jobs or maps them onto catalog episodes (that 0.0.2 experiment is reverted).

Changed (title-finding realignment)

Restored the August 17 grouping engine (bb1cdb1 / 0692b2d), not the later Units / batched Group / LLM SxxExx experiments.

  • Immediate children only (scan.Children). One match.Group per child. Compact listing, ~5 filename samples, max_tokens: 256.
  • Append + Kick inside the child loop so matching overlaps later grouping.
  • Prompt is the August 17 rules again (folder as title, one object per series, no SxxExx, extras are not shows).
  • Added Path: on the listing and {title,year,path} so a mixed folder can use {Path}/{child} (movie/OVA next to seasons).
  • Parent: on folder listings; fallback title prefers Parent when Folder: is Season N.
  • Same title from two children is still two jobs (no end-of-scan merge).

Added

  • Server-minted session ids: <UTC 20060102T150405Z>-<16 hex>.
  • GET /v1/sessions — unexpired ids, newest first.
  • session.ttl_ms (default and max 24h). Expired job files are purged; catalog on disk is not.
  • GET /v1/catalog?session= lists only titles that session matched.
  • DELETE /v1/catalog and DELETE /v1/catalog/{provider}/{id} — no session; 409 if any live session still has that match.
  • Admin UI stores the last session and sends ?session= on job/scan/catalog calls.

Fixed

  • Image Fetch no longer shares the provider API pacer (tvmaze/poster no longer queues for minutes behind search).
  • Admin scan poller no longer dies on the previous session while POST /v1/scan is in flight; poller restarts after setSession.
  • POST /v1/scan does not walk the whole tree before 202; files is the sum of each child’s video count.
  • ListVideos / samples follow symlink library roots.

Removed

  • Units / FolderClass / batched GroupUnit / LLM file S/E labels.
  • Unused MergeJobs / empty Grouped.Files leftovers.

v0.0.2

v0.0.2 Pre-release
Pre-release

Choose a tag to compare

@AlyShmahell AlyShmahell released this 28 Aug 23:49

0.0.2

Scan jobs now carry video files and map them onto catalog episodes. Grouping stays one instruct call per library child (prompt.md); season/episode labels are only taken from explicit SxxExx in the filename.

Added

  • Job files (path, optional season / episode) filled during scan from videos under each child.
  • Catalog episodes get path / paths when file S/E matches the catalog numbers (01 = 1).
  • Same title+year in one scan merge into one job (Season 1 + Season 2 of one show), with path set to the show folder.
  • Season-folder listings include Parent: so a failed group falls back to the show name, not Season 1.
  • Vendored llama-server is started with PR_SET_PDEATHSIG SIGTERM so it exits if Matchora’s PID disappears. Clean Stop() still SIGTERMs the process group.

Changed

  • prompt.md may return files on each show. Paths must be copied from the listing; do not guess S/E from 01.mkv or a Season N folder.
  • browse_root: / treats absolute paths as inside the root.

Notes

  • Ingest is unchanged (ingest.md). Catalog join is still numeric, not a second LLM call.
  • share/config/default.yaml version is still 0.0.1; bump it if the shipped YAML should match VERSION.

v0.0.1

v0.0.1 Pre-release
Pre-release

Choose a tag to compare

@AlyShmahell AlyShmahell released this 28 Aug 13:02

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.

[0.0.1] - 2026-08-28

First public release. Matchora is a Go service that ingests title rows or scans a
video library, searches YAML-defined metadata APIs, and ranks candidates with a
local llama.cpp embedder or instruct model.

Added

Matching and providers

  • Generic GET + JSON-path provider engine. Provider names are not hardcoded in Go.
  • Shipped providers: TVMaze, Jikan v4, OMDb (movies, keyed), TMDB movie and TMDB TV (keyed).
  • Type allowlists so a typed job only calls providers that list that type.
  • Fast pass plus deferred providers (defer: true) when hit count or best score is too low.
  • Parallel provider GETs per title; per-job HTTP clocks so a slow deferred call does not stall the batch.
  • Per-provider pacing (min_interval_ms), retries, per-attempt timeouts, capped exponential backoff, and Retry-After.
  • Provider cooldown after consecutive GET failures (jittered skip, reset on success).
  • Ranking: MiniLM embeddings (cosine) or instruct chat; lexical token overlap + year bonus if the embed server is down.
  • Auto-matched vs manual vs unmatched from min_score / min_margin; user confirm via select.
  • Season/episode catalog fetch for TV providers (TVMaze, TMDB TV), including a catalog-only request that does not change the match.
  • Optional provider detail GET (OMDb plot) and poster downloads.
  • skip_episode_posters on ingest, scan, rematch, retry, select, and catalog.

Ingest and scan

  • POST /v1/ingest for CSV (header) or JSON arrays: title, year, type, season, episode, imdb.
  • CSV alias map (ingest.aliases) and type rewrite (episode/season → tv).
  • Instruct fallback to map unknown CSV columns using config/ingest.md.
  • POST /v1/scan lists videos under browse_root, groups dirty names with a local instruct model (config/prompt.md), then matches in the worker.
  • Scan grouping progress via GET /v1/scan/status.
  • Expected Plex / Jellyfin / Infuse trees (movies vs series roots, optional {tmdb-…} / {imdb-tt…} ids).

Catalog

  • Matched, selected, and cataloged titles written under {data_dir}/catalog as [uniqueid-id] Title (Year)/ trees.
  • tvshow.nfo / movie.nfo, season folders, episode .nfo, and downloaded posters.
  • Distinct uniqueid slugs so TMDB movie vs TV ids cannot collide (tmdb-movie, tmdb-tv, OMDb imdb).
  • GET /v1/catalog and GET /v1/catalog/{provider}/{id}; posters at /poster.jpg (and season/episode variants).
  • Clearing jobs does not delete the catalog tree.

Runtime and llama.cpp

  • HTTP admin server on :7680 (User-Agent: matchora/{version}).
  • Writable {exeDir}/data: jobs.json, optional config.yaml overlay, secrets (YAML map of API keys).
  • GET/POST /v1/secrets report which key slots are set and merge the secrets file (values never returned). Slots are provider API keys only.
  • GET/POST /v1/config read and deep-merge {data_dir}/config.yaml (same shape as default.yaml). Never includes secrets.
  • Listen address is llama.host / llama.port (defaults 127.0.0.1 / 8080); llama.base_url is derived as http://{host}:{port}/v1.
  • On start, probe that URL. A healthy listener is left alone. If it is down, install llama.cpp + GGUFs into {exeDir}/vendor/llama.cpp and spawn one llama-server on 127.0.0.1 at that port (embed + optional instruct). Distinct llm_base_url (e.g. a stub) is left alone.
  • After a successful secrets or config POST, return the JSON body, stop a spawned llama-server, and re-exec so the next Load / llama.Start applies the files. Setters write files only.
  • --prepare runs that install, verifies models, stops the spawned server, and exits.
  • Dist is binary + config/ + public/ only; llama.cpp is not in the slim tree.

Admin console

  • Verification UI at / (folder picker via GET /v1/fs, ingest upload, scan, job cards).
  • Secrets panel for provider keys and llama panel for host/port; both wait for /health after the process restart.
  • Live match wait log, sticky status chips, score heatmap, retry errors/unmatched, clear jobs.
  • Manual candidate select and per-candidate seasons catalog.
  • Skip-episode-posters checkbox persisted in localStorage.

HTTP API

  • GET /, /health, /v1/fs, /v1/jobs, /v1/match/log, /v1/scan/status, /v1/catalog, /v1/catalog/{provider}/{id}
  • GET/POST /v1/secrets, GET/POST /v1/config
  • POST /v1/ingest, /v1/scan, /v1/match, /v1/retry, /v1/jobs/{id}/select, /v1/jobs/{id}/catalog
  • DELETE /v1/jobs (empty list and abort in-flight scan grouping)

Build, package, and tests

  • Podman linux/amd64 dist builder (./build/run: run, rebuild, prepare, package).
  • Slim tarball matchora-*-linux-amd64.tar.gz (binary, config, public, LICENSE).
  • Bundled tarball matchora-*-linux-amd64-llama.tar.gz (same plus llama.cpp, GGUFs, and fetched third-party licenses).
  • Podman-only unit, smoke (stub metadata + stub chat; waits for /health after secrets POST), and optional live (MATCHORA_LIVE=1) harness.
  • BSD 3-Clause license.