Repository navigation
Releases: ptitzgeg-on-git/vauxtra
Release list
v1.7.0
Docker Image
docker pull ghcr.io/ptitzgeg-on-git/vauxtra:1.7.0Verified pullable on both linux/amd64 and linux/arm64 before this page was created.
Changelog
Changed
-
A new visual identity. A petrol-teal palette, flat surfaces and a new logo replace the
neon-on-black look. The README shows the interface with a banner and screenshots taken on
a fresh demo instance. -
Dropdowns, checkboxes and chips belong to the design. Every
<select>is now a
themed listbox that keeps the native keyboard (arrows, Home/End, type-ahead, Escape that
closes the list without closing the dialog around it). The domain field of the service
form suggests the known zones in the same list instead of the browser's grey<datalist>.
Tags and environments are checkable chips. -
One word for what the operator creates: "service". The interface said service,
endpoint, route or host for the same thing depending on the screen. 112 strings changed in
the eight languages, and the locale guard now pins the word in each of them. The menu
group that holds services, tunnels and certificates is called "Publishing". -
The installation and the add forms no longer contradict themselves. The wizard, the
integration form and the service form were reworded step by step: buttons say what they
do, the integration name is asked first in the guided flow, and "Settings" is no longer
listed twice.
Fixed
-
Setting a password in the setup wizard ended the wizard. The server stops answering
"setup required" as soon as a password is set, and the application showed the wizard only
while it did: the recommended path landed on an empty dashboard after the second screen,
and a reload after the first integration did the same. The wizard now marks itself started
in the tab and stays until it is finished. Its restore screen, which asks for no typed
confirmation, is still offered only while the server calls the instance unconfigured. -
Every push to Nginx Proxy Manager removed a host's own certificate. The certificate was
looked up by the service's zone,example.com, instead of its name,vault.example.com,
so a certificate issued for the host itself -- NPM's default, one per host -- was never
found, and the update wrote the host without one: HTTPS was gone after every push, edit or
reconcile. The update also wrote the whole host, resetting the aliases, custom locations,
HSTS, exploit blocking and meta the operator had set in NPM. The lookup now takes the full
name, and the update reads the host and sends only what Vauxtra owns: the forward target,
websockets, the service's name and a certificate that covers it. A certificate the lookup
cannot judge is kept; one that does not cover a renamed host is removed. Zoraxy used the
same lookup and is fixed with it. A wildcard is no longer offered for the zone's own name,
which it does not cover.
Security
brace-expansion, a development dependency of the lint tooling, is raised to 1.1.21 and
5.0.12 (GHSA-q2hr-2g5m-vwhr, GHSA-qhr7-859c-m2p7, GHSA-6j4f-fj2g-mc7p). It is not part of
the image.
What's Changed
Other changes
- release(main) : promotion de dev, la 1.7.0 avec elle by @ptitzgeg-on-git in #123
Full Changelog: v1.6.1...v1.7.0
v1.6.1
Docker Image
docker pull ghcr.io/ptitzgeg-on-git/vauxtra:1.6.1Verified pullable on both linux/amd64 and linux/arm64 before this page was created.
Changelog
Fixed
-
A Cloudflare DNS record came back grey, a CNAME to a tunnel included. Every write
through the Cloudflare DNS integration took the orange cloud from the integration's
setting, which is off by default, and every CNAME was written grey whatever it pointed at.
A CNAME to<tunnel id>.cfargotunnel.comonly answers when it is proxied. Measured on a
production instance: a test name Vauxtra had just created toward a tunnel answered a CNAME
and no address through two public resolvers, while the proxied record beside it, same
target, answered two. Creating, renaming, re-enabling or correcting the drift of such a
name cut it off. A proxied A record whose address changed came back grey without a word,
and the origin's address went into public DNS: the scheduler's address update rewrites a
record in place, the push corrects a drift by removing it and adding it back, a rename
creates the new name, and each wrote the default over the operator's choice. A record
already there now keeps its flag when its address changes, a rename carries the old name's
flag, and a record the push removes and adds back gets its own flag back. A CNAME to a
tunnel is created proxied, and one found grey is turned orange. Any other CNAME is still
created grey, since a proxied one pointing at a name on another Cloudflare account answers
error 1014, and for the same reason an address's flag never crosses to a CNAME. The
Cloudflare Tunnel integration writes its own CNAME proxied and was not affected. Known
limit: disabling a service and enabling it again removes the record in one request and
creates it in another, hours or days apart, and nothing keeps the flag in between, so a
proxied A record comes back with the integration's default. A CNAME to a tunnel comes back
proxied. -
After an upgrade, the browser could show the previous build.
index.htmlnames the
hashed bundles of its build under a name that never changes, and it was sent with a
Last-Modifiedand noCache-Control, so a browser could reuse its copy without asking
for a fraction of its age (heuristic freshness). Measured on a production instance: a page
loaded just after an upgrade came out of the browser's cache with the previous build's
bundle while/api/healthalready answered the new version, and the screen showed a
defect the running version had fixed. Every file of that route, the page and the icons at
the root of the build, is now sent withCache-Control: no-cache: the browser keeps its
copy and asks again before using it. The hashed bundles under/assetsdo not go through
that route and are unchanged. -
Every move to another proxy came back with an error about the host it had just
removed. A regression of 1.6.0. The edit deleted the previous proxy host in its proxy
block, then again with the former targets, whose failures 1.6.0 had just started to put in
errors. NPM answers the second deletion of an id with a 404, so the save reported
"Former target: Failed to delete proxy host" about a host that was gone. The previous host
is now deleted once, with the former targets. Found by reading the code and reproduced by
tests. -
An edit that moved a service did not say when the previous route stayed up. Moving a
service to another mode, integration, name or tunnel publishes the new route, then
withdraws the old one. Those withdrawals dropped the provider's answer, and an exception
reached the journal only: a provider that refused left the old route serving the name
behind a 200 with an emptyerrors, while the screen showed the service moved. One of
them already read the answer, the tunnel route of a service disabled in the same request
that changed its name or its tunnel. A bulk enable had the mirror defect: it wrote "DNS
re-added" to the journal whatever the DNS server answered. A refused withdrawal is now
reported, in the response and in the journal, once it has been read against the
provider's listing: NPM answers a 404 for an id it no longer holds and the Cloudflare DNS
integration answers false when nothing matches, and neither is a route left behind. A
listing that cannot be read does not clear the refusal. Cloudflare Tunnel is read against
nothing: its ingress half answers true for a rule already gone, so a false from it is
always a failure, possibly of its DNS half, which the rule listing does not show. A
read-only integration is no longer called, and a proxy that files its hosts by name is
asked about the service's name rather than what an earlier rename left in the database. A
bulk enable reads the DNS server's answer and reports a refusal, and an exception raised
while an edit switches a proxy host on or off lands inerrors, not only in the journal.
A disabled service no longer reports, on every edit and on its deletion, a DNS record its
disable had already withdrawn. Every provider exception that reacheserrorsis masked,
as the journal already was. No integration shipped today lets a credential through there:
the two that authenticate in the query string catch theirrequestserrors on writes and
mask their listing refusals, and the masking closes the path for the next one. Found by
reading the code and reproduced by tests, not observed on a running instance. Known
limits: when the DNS server and the address change together, one refused record can be
reported twice, by the withdrawal from the old server and by the one from the former
targets; and deleting a service does not yet read its proxy's listing, so a host removed
by hand is reported as a failure.
What's Changed
Other changes
- release(main) : promotion de dev, la 1.6.1 avec elle by @ptitzgeg-on-git in #119
Full Changelog: v1.6.0...v1.6.1
v1.6.0
Docker Image
docker pull ghcr.io/ptitzgeg-on-git/vauxtra:1.6.0Verified pullable on both linux/amd64 and linux/arm64 before this page was created.
Changelog
Fixed
-
The provider scan offered zones nobody had declared, and never said which rows sat in
them. Measured on a production instance with one DNS domain declared: a first scan listed
91 routes, 59 of them in twelve zones the operator had never declared, behind a "Quick
Import (91 new)" button that writes them all after a single confirmation. A second scan,
minutes later, with 32 services imported in between, listed 32. That looked like a
declared-domain filter that only engaged once a service existed. There was no such filter
anywhere: the scan returned whatever each integration could read. The instance's journal,
read afterwards, rules out a failed listing: none failed on either pass, no integration was
edited in between, and the Zone ID field was empty. By elimination, what changed is what
the token could read: its zone resources were narrowed on the Cloudflare side between the
two scans, and it now reads the declared zone only. That part is not a code defect. What
the code did get wrong is that its answer could show none of it. No row said which zone it
belonged to or whether that zone was declared, integrations were read in no set order, and
an integration whose listing raised was written to the journal and nowhere else, so a
failed listing would have looked the same: fewer routes, without a word. The scan now files
each row under its zone (the longest declared domain it sits under, then the zone the
provider reports, then its first dot), says whether that zone is declared, returns the
declared domains and one line per integration with how many entries it listed or why it
could not, and reads the integrations in the order they were added. Settings > Data opens
on the declared domains, says how many routes that hides, marks an undeclared zone on its
row, filters by zone, and names an integration it could not read, with a link to the
integrations page. -
The import split a name at its first dot.
nas.maison.example.orgbecamenasunder
maison.example.org, whatever zone it came from. The check for a service already tracked
under that name used the same split, so a service stored asnas.maisonunder
example.orgwas not found: the import created it a second time and declared
maison.example.orgas a domain on the way. The import now splits at the zone the scan
filed the row under, finds a tracked service by its whole name and by its tunnel hostname,
and refuses the apex of a zone it knows, declared or reported by the integration, with the
reason on the row, as it already refused a name with no dot. The apex used to go through
the same split as any name:example.netbecame a serviceexampleunder a domainnet,
declared on the way. A name whose zone neither the declared domains nor the integration
give still falls back to its first dot, apex included. -
Linking a DNS record overwrote the DNS half a service already had. When a scanned
record matched a tracked service, the import set the service's DNS integration and address
to the record's, whatever the service already had: a service published through one DNS
integration was moved to another, and a tunnel service was given a DNS integration it does
not use. A link now fills only the half that is missing, and a service that already has a
DNS integration, or is published through a tunnel, is set aside with the reason. When two
integrations answer for one name, the message says the other record is left as it is and
not tracked: expected with split-horizon DNS, a stale record otherwise. -
"Quick Import" sent the whole scan back. Rows already tracked and rows in zones nobody
had declared went with it, and its confirmation counted them without a word about zones.
A tracked row sent back is what let a DNS record re-link a service, as described above. It
now sends the importable rows on screen and nothing else, and of a row that links a tracked
service, only its DNS record. Its confirmation says how many tracked services will be
linked, how many routes sit in zones not declared, how many names several DNS records
answer for, how many use a local TLD, and how many importable routes the filters leave
out. Both import confirmations now open on Cancel. The setup wizard's import step shows
one row per name and, since importing a service declares its domain, names the domains an
import is about to declare, above the button that does it. -
An Enter held down could answer a confirmation. A dialog of the
infovariant opens
with the focus on its confirm button, and a browser keeps firing an Enter that is held
down: the Enter that asked the question came back as a repeat on the confirm button and
answered yes. A repeat is now ignored inside the dialog, on its buttons and in its typed
confirmation alike, and a caller can ask for the focus to open on Cancel (initialFocus).
The report of an import confirmed without its button being clicked was not reproduced, and
this is one way it can happen, not a proof that it did. The other ways the report suggested
a question could end are now pinned by tests: the focus leaving the dialog or the window
answers nothing, the page that asked going away answers no, and a second question answers
the first one no. -
"Test all" on the Integrations page could sit through a whole health round before testing
anything. It refetched four queries before sending a single test, one of them the health
round that tested every integration one after the other. A refetch joins a first load still
in flight, and cancels and restarts any later round, so the click waited for a whole round
either way, the button busy and disabled all along. That explains a click that sent no test
for a while, not by itself the report's three clicks that sent no request at all: only a
refresh already running before them would silence those, and nothing in the report says one
was. It was not reproduced against the instance the report measured. The tests now go out
at once: each card spins until its own answer comes back, the button counts them, and with
no integration enabled the page says there is nothing to test instead of reporting a
refresh. The health round itself now tests up to eight integrations at a time. -
"Scan providers" said nothing about the domains it files names under. The report
measured a button that sent no request until a DNS domain was declared. That was not
reproduced: with no domain declared the button sends its request, and no code path holds it
back. What was missing is the reason a declared domain matters. The panel now says, before
the scan, that it files names under the declared domains and that none is declared, with a
link to Settings > DNS Domains; it does not claim so while the list of domains could not be
read, and the declared-only filter stands aside while there is nothing to filter on. -
The provider wizard called an empty form complete, and asked for the name last. "Name
the integration" folded the guided steps away and showed "Everything is filled in. Validate
the connection to continue." above an empty address, username and password. The name is
now asked first, in every mode. "Next" waits for the required fields of its step, the step
dots cannot jump past the first step still missing something, and the last step lists what
is missing, each field asked on an earlier step linking back to it, and says everything is
filled in only once it is. Behind the banner, the forms disagreed on what is required: the
guided steps marked the NPM e-mail required, the expert form of the same dialog called it
optional, the dialog's "Next" and step dots checked nothing, and the e-mail could be left
blank all the way to a failed login. One rule now serves both forms and the setup wizard: a
field is required when the local rules say so, or when a guided step asks for it without
marking it optional. That adds the username of NPM, AdGuard Home and Technitium, whose
login cannot go without one, and an edit is held to it too: only the secret may stay blank
there, to keep the stored one. An optional field is marked once, where five labels also
said "(optional)". In the setup wizard, picking another type gives the form that type's
name and an empty address instead of the previous type's, a connection verdict is dropped
by the next edit so "Connect" never answers for values nobody tested, and a hosted API's
address field now shows in the expert form, optional, saying what a blank one does. -
A DNS record with no port was probed on port 80 and reported down. The import gave a
DNS record port 80, so a name published in DNS alone, an A record for a UDP service or a
CNAME to a tunnel, was probed over TCP on a port nothing listens on and turned red: on the
instance measured, two of the three services reported down were exactly that. A service's
port can now be 0, meaning none. The import writes 0 for a DNS record, the "DNS only" form
accepts an empty port and no longer refills it with 80 when the field loses focus, and
nothing probes such a service: not the scheduler, not "Check all services", which counts
it apart from the tunnels in its summary, and not "Check now", which still resolves the
name and says there is no port to test. The server refuses 0 where a proxy or a tunnel
forwards to the port, and saving a service without a port resets its status to unknown.
Monitoring writes "No port: not testable" on its row and explains it in the drawer, and the
latency card counts only the latencies actually measured, where it used to read "Measured
on 1 service this session" under a dash after a check that measured nothing. A service
impor...
v1.5.2
Docker Image
docker pull ghcr.io/ptitzgeg-on-git/vauxtra:1.5.2Verified pullable on both linux/amd64 and linux/arm64 before this page was created.
Changelog
Fixed
-
A diagnostic check with no detail announced itself with a generic word. When a
provider validation check returned no detail sentence, both screens that show it fell back
to "Verification" — the same word for every check in the list. The name of the check was
in hand the whole time and was never used. The check names are now translated in all eight
locales, and a folding helper resolves them, so the fallback reads "Token verification"
where it used to read "Check". Counted, not assumed: the providers emit 29 check names,
which fold to 28 keys becausedns_writeandDNS writeare the same check spelled two
ways, and all 28 exist in every locale, with no key left over for a check nobody emits. A
name this build does not know — what an API newer than the build sends — shows through as
itself, which tells a reader more than a generic word would. The generic word is what
remains for a check carrying no name at all. -
The log footer counted rows the screen was not showing. The level filter is applied by
the server and the text search only sifts the page already in hand, and both fed the same
footer sentence. The count shown was therefore the server's, never the screen's. The footer
now tells the two cases apart and says how many rows match when a text search narrows the
view; the empty state no longer blames the API on a branch only reached after a request
succeeded, and reads "no filter in effect" instead. The counted key the sentence needs,
settings.logs.total_matching, did not exist and arrives here in all eight locales, with
the plural categories each language actually has. -
French set a breakable space before its punctuation. French puts a no-break space
before? ! ;and around its quotation marks, and the browser is the reason: a plain space
there lets the?wrap onto a line of its own, under the sentence it belongs to. A narrow
no-break space was substituted for one breakable space in 221 values — one of which also
had a word corrected — and folding both files back to plain spaces returns them byte for
byte identical, which is the proof that only spaces moved. The pass was applied by a script
that masks{placeholder}tokens and technical runs such ashost:8443andhttp://…
before it matches. That mask now lives inscripts/locale-typography.mjs, out of the guard
so it can be imported at all, and is pinned by twelve witnesses: five sites it must take,
five it must refuse, two on the mask itself. Each escape hatch was watched going red with
the hatch removed. Measured while writing them: not one French value in the file needs that
mask today. It is there for the sentence written next month, which will carry a port or a
placeholder. -
A log level badge read "D'accord". The severity labels on a log line had been
translated as ordinary words instead of as levels:OKread "D'accord" in French, "bien"
in Spanish, "Okay" in German and "Oke" in Dutch, and the Spanish error level read "error"
in lower case while every other level was capitalised. These are badges on a journal entry,
not an answer to a question. All four now readOK, which is what the level is called in
each of those languages, and the Spanish error level matches the case of its neighbours. -
French settings pages said "providers". Eight French values had kept the English word
where the rest of the interface says "fournisseurs": the migration panel, the webhook scope
picker and the credentials export help among them. A ninth, the journal's empty state, had
lost the accents from "deconnectee". All nine now read as French. -
The container could still regain a privilege it did not start with.
no-new-privileges
had been left off both Vauxtra services by reflex, because the entrypoint starts as root.
That is the wrong reflex: the flag forbids REGAINING a privilege, not dropping one, and
what the entrypoint does is the drop. Measured on the image with the flag set:groupadd,
usermod,chown -Randgosu appuserall four go through, and the Docker socket's
secondary group arrives on the other side. Deliberately no further:read_only: trueand
cap_drop: ALLboth break this entrypoint, which writes/etc/groupand/etc/passwdand
chowns/app/data. That negative knowledge is written beside the flag, so the next reader
does not set them and find the breakage at the following restart.
Added
-
The locale guard now refuses French written without its accents. The list is narrow on
purpose: a word only enters it when its unaccented spelling is never correct French. That
is whya,ou,surandexposeare absent — they are real words, and listing them
would make the guard noisy enough to be switched off. It reads\p{L}rather than\w,
because JavaScript's\wstays ASCII even under/u, which would cut "Parametres" into
"Param" and "tres", and "tres" is on the list. -
The locale guard now refuses a breakable space before French punctuation. It runs on
fr.jsonalone, and that is the point: this is a French typographic rule, and applying it
to the other seven locales would flag correct text. The message names the punctuation whose
space is wrong rather than a column number, so it says what to repair.scripts/carried no
test at all before this; vitest now collectsscripts/**/*.test.mjsas well. -
The locale guard now pins three words that are only wrong in one language.
Prestations
is what a caterer sells,Balisesare the tags in a markup document,Dienstleistungenis
the commercial sense of "services", andProviderwas English sitting in a Chinese file.
Each was the only string in its file using that word while the rest of the file said
otherwise. The per-locale table and the global one are concatenated rather than spread: both
are keyed by locale key and hold a list of words, so{ ...global, ...perLocale }would let
a per-locale list REPLACE the global one for that key instead of adding to it, and the
global ban would disappear without a word. No key sits in both maps today; that line is what
keeps it harmless when one does. -
Four assertions on the sentence the expose assistant prints at the end. CI already
prevents the cause — a translation call that fills none of its parameters fails the build
— but nothing looked at the render. Putting a spare<span>{host}</span>back printed the
host twice on screen with every guard green. The four checks say: the host is written once,
there is a single monospace region whose text is exactly the host, the translator's word
order is honoured when the parameter moves, and the case of a translation that has lost its
parameter, described asdevbehaves. The negative witness was played: with the defect put
back, the four new tests fall and the nine existing ones stay green.
What's Changed
Other changes
- release(main) : promotion de dev, la 1.5.2 avec elle by @ptitzgeg-on-git in #109
Full Changelog: v1.5.1...v1.5.2
v1.5.1
Docker Image
docker pull ghcr.io/ptitzgeg-on-git/vauxtra:1.5.1Verified pullable on both linux/amd64 and linux/arm64 before this page was created.
Changelog
Fixed
-
Two different schemas declared themselves under the same number. 1.5.0 added an
eighteenth migration,service_templates.environment_ids_json, without touching
SCHEMA_VERSION, although the comment above the constant asks for exactly that.
Measured on the production instance right after the upgrade:settings.schema_version
read 11 and the column was there. A 1.4.0 database and a 1.5.0 database therefore
answered the same 11, and the one marker that tells two schemas apart after the fact
told nothing apart. The constant is now 12, and the guard meant to make that omission
visible no longer forbids it: it frozeassertEqual(11, SCHEMA_VERSION), so bumping
the constant broke the suite. It checks the pair (number of migrations, version) now,
and says what to do when it falls. -
A real HIGH finding was reported as a scanner that had not run. The grype exit
codes in the security workflow were read backwards. 2 is
ErrAboveSeverityThreshold, which is the tool succeeding and finding something; 1 is
the tool failing. So a genuine finding came out as a broken run, and a genuine crash
came out as a clean report. The codes are now taken from the source. -
The release tag was validated by a glob.
v[0-9]*.[0-9]*.[0-9]*accepts
v1.5.0-rc1andv1.5.0+meta, which the error message directly below it promises to
refuse. And because the shell's*matches a newline, a two-line value passed too:
the twoecholines that follow write to$GITHUB_OUTPUT, which GitHub reads line by
line, so the second line published an output nobody wrote.versionis precisely the
string the release page hands the reader to paste intodocker pull. An anchored
regular expression refuses all three shapes. -
The release wait could not reach its own deadline. The job ceiling was 20 minutes
for a wait that can legitimately last 30, so the two::error::lines that name the
missing manifest were unreachable code. The ceiling is 40 now, and the wait reads its
budget fromPULL_WAIT_MINUTES, written once. -
Two publication runs that overwrite each other were filed apart, then all of them
were filed together. The concurrency group ended ingithub.ref, which put the two
runs that collide into different groups: pushingmainand then tagging that same
commit is two refs and one commit, and both writelatest. Measured on v1.5.0: four
minutes of overlap, and it was the second to finish that placedlatest. The first
repair put every publication into one group, which was too wide: GitHub cancels a
group's queued run as soon as a newer one queues behind it, whatever
cancel-in-progresssays, so a push todevarriving while a release build waited
killed that build, and the release then spent its whole wait on an image nobody was
building.mainand a release tag now share one queue;dev, which moves onlydev,
has its own. -
The
sha-tag was written from every ref, although the images differ.APP_VERSION
islatest,devor the version number depending on the ref, so the second run
repointed the crumb and left the first image nameless, handed to the weekly cleanup's
delete-untaggedalong with its signature and its attestation. It is written from
mainonly. -
The GHCR cleanup never verified
dev. It protects the tag from deletion, because a
staging instance pulls it on every restart, and then asked "does this tag still
resolve?" oflatestand the version numbers only. The one moving tag with a machine
behind it was the one nobody checked. The contract holds 13 tags now instead of 12. -
Two progress logs never arrived in time. The wait for the image, up to thirty
minutes, and the walk of the registry, some fifty manifests, run underpython3 -u
now. Python buffers its output whenever it is not a terminal, and under Actions it
never is: measured here, three lines written over 1.2 s arrive at the far end of the
pipe within 91 ms, all at process exit. The log worth reading is the one from a run cut
short by its own ceiling, and that was exactly the run that wrote nothing. -
The hygiene gate swallowed the files it could not read. Its conclusion is about the
whole repository; a file it failed to decode is a file it did not look at, and
Repo hygiene check passedcame out with the same confidence. Such a file is named
now. Measured: all 400 tracked files decode today, which is the reason to close the
hole today rather than the reason not to. -
The version gate read two declarations out of four. It pinned
vauxtra_mcp/__init__.pyagainstfrontend/package.jsonand against what the MCP
handshake actually answers, and stopped there. The release number is also written twice
infrontend/package-lock.jsonand once at the top of this file, and neither was
watched. Both were found by grep while cutting this very release, not by a failing
test, which is the defect family that gate exists to catch. A stale lock does not build
a wrong image, it failsnpm ciwith a message about being out of sync, a long way
from the character that needs changing; a stale changelog publishes a release page for
a build nobody made. Three tests now read the lock by key, the newest released heading,
and the comparison link that turns that heading into something clickable. Reading by
key is not a style choice:css.escapein that lock is at 1.5.1 andlz-stringat
1.5.0, so a text search for either number finds theirs first.CONTRIBUTING.mdnames
the four files too: its release instructions went straight from "whendevhas proven
itself" to "tagmain", with the cut that has to happen in between written down
nowhere.
Removed
- The
UNRECOVERABLEexclusion set in the GHCR cleanup. Its six tags have been taken out
of the registry, read anonymously on 2026-09-20: not one of the six is there, so it
excluded nothing and its::notice::no longer printed. A silent exception list that
matches nothing is worse than none, because it would wave through the next breakage
that reused one of those names.
Changed
- Dependencies:
uvicorn[standard]>= 0.53.0,apprise>= 1.13.1,pytest>= 9.1.1
for development, four GitHub Actions and fifteen npm packages in the minor group.
What's Changed
Other changes
- fix(release) : la publication mourait sur ses propres notes, deux fois by @ptitzgeg-on-git in #100
- release(main) : promotion de dev, la 1.5.1 avec elle by @ptitzgeg-on-git in #107
Full Changelog: v1.5.0...v1.5.1
v1.5.0
Docker Image
docker pull ghcr.io/ptitzgeg-on-git/vauxtra:1.5.0Verified pullable on both linux/amd64 and linux/arm64 before this page was created.
Changelog
This version's changelog section is 287183 characters, more than a release page can hold. Read it in full in CHANGELOG.md.
What's Changed
Other changes
- ci(branches) :
mainwas both the draft and the production branch by @ptitzgeg-on-git in #84 - release(main) : promotion de dev, 124 commits, zéro CVE ouverte by @ptitzgeg-on-git in #95
- release(version) : 1.5.0, le dépôt annonçait encore 1.4.0 by @ptitzgeg-on-git in #99
Full Changelog: v1.4.0...v1.5.0
v1.4.0
v1.0.0
ℹ️ This version was never shipped. The tag was created on 2026-09-11 so the numbering has
no hole in it, and it points at the same commit asv1.0.1.PR #20 was squashed on merge, so
app/security.py,app/cache.py,app/errors.pyand the
operator guides all reachedmainin a single tree, which went out as 1.0.1 two days later.
There is no state of the repository that corresponds to 1.0.0 alone, and no1.0.0image was
ever built or pushed.Use
v1.0.1if you are looking for this code. Its release notes explain why its image cannot
be pulled either.
What was in it
Added
app/security.py— CORS origin validation, domain sanitization, password strength enforcementapp/cache.py— request-scoped caching with TTL expiration, removing N+1 provider callsapp/errors.py— unified error handling with standardized codes across every endpoint- Operator documentation:
docs/HOWTO.md,docs/DEPLOYMENT.md,docs/TROUBLESHOOTING.md
Changed
- CORS validation: strict origin checking, no wildcards, port and scheme enforcement
- Error responses: standardized codes and shapes across all API endpoints
app/main.py— CORS validation and request cache middleware
Fixed
- 8 security issues (CORS, domain injection, password policy, session handling)
Docker Image
None. This number was never built. The nearest published image is 1.1.0, which is the oldest
one that still pulls:
docker pull ghcr.io/ptitzgeg-on-git/vauxtra:1.1.0Full Changelog: v0.1.0...v1.0.0
v1.3.0
Docker Image
docker pull ghcr.io/ptitzgeg-on-git/vauxtra:1.3.0What's Changed
- fix(ci) : le SBOM n'essaie plus de se coller à une release qui n'existe pas encore by @ptitzgeg-on-git in #58
- chore(release) : v1.3.0 by @ptitzgeg-on-git in #59
Full Changelog: v1.2.0...v1.3.0
v1.2.0
Docker Image
docker pull ghcr.io/ptitzgeg-on-git/vauxtra:1.2.0What's Changed
- Audit de sécurité v1.1.0, fournisseur Zoraxy et refonte de l'interface by @ptitzgeg-on-git in #45
- fix(pihole): rendre le siège d'API après chaque opération — et verser le banc qui l'a trouvé by @ptitzgeg-on-git in #54
- chore(release): v1.2.0 by @ptitzgeg-on-git in #52
Full Changelog: v1.1.0...v1.2.0