- Home Assistant Integration - Per-user webhook credentials for pushing trade and wishlist-price alerts to Home Assistant, and pulling live collection stats.
- New Webhooks page where any user can generate credentials, each pairing a Home Assistant target URL with a bearer secret shown once at creation.
- Push events for trade proposals, updates, engagement, acceptance, and rejection/cancellation, plus a wishlist target-price-met alert.
- A new authenticated pull endpoint for Home Assistant to fetch a user's collection stats on its own schedule.
- Per-credential TLS verification toggle for Home Assistant instances behind a self-signed certificate or internal CA, with a visible "Insecure" indicator when disabled.
- Admin controls in Settings to enable/disable the integration and cap credentials per user (default 3, off by default).
- See the wiki for setup and Home Assistant configuration details.
- Version bumped from 1.9.2 to 1.10.0 within
constants.py.
added_at,created_at,recorded_at, andlast_fetchedcolumns (cards,users,collection_entries,decks,price_history,wishlist_entries,converted_currencies) usedserver_default=sa.text('now()'), valid Postgres SQL but not a SQLite function, causingsqlite3.OperationalError: unknown function: now()on any insert that didn't explicitly set the timestamp (e.g. importing a card) when running on SQLite- Migrations now use
sa.func.now(), which SQLAlchemy compiles per dialect (CURRENT_TIMESTAMPon SQLite,now()on Postgres); new migrationc3d4e5f6a7b8repairs existing SQLite databases in place viabatch_alter_table, applied automatically on nextalembic upgrade head
- Version bumped from 1.9.1 to 1.9.2 within
constants.py.
-
Deck Usage Tracking - Shows how many copies of a card printing are currently allocated to decks.
GET /collectionreturns a newin_decksfield per entry: the sum ofDeckCard.quantityacross all of the user's decks (mainboard, sideboard, and commander) for that card's printing.- Counted per printing (
card_id), not per foil/condition/language entry.DeckCardhas no foil field, so a foil and non-foil row of the same printing report the same count. - Collection page: "N in decks" shown below the Qty value.
TradeAddCardModal- "N in deck(s)" badge shown per card in the browse list, and a note next to the quantity picker once a card is selected.
-
SQLite Database Support - PostgreSQL remains the default;
docker-compose.sqlite.ymlruns the app against a local SQLite file instead. Permanent, per-instance choice - there is no tool to convert an existing instance between backends.backend/database.py- exportsDB_BACKEND; registersPRAGMA journal_mode=WALandPRAGMA foreign_keys=ONon every SQLite connection.GET /auth/setup-requiredreturnsdb_backend; the Setup page banner names the active backend.POST /auth/setupwrites$CONFIG_PATH/db_backend.lockafter the first admin account is created.entrypoint.shcompares this against the currentDATABASE_URLon every boot and refuses to start on a mismatch.docker-compose.sqlite.yml- standalone compose file (docker compose -f docker-compose.sqlite.yml up) with its ownSQLITE_PATHvolume.docker-compose.ymlitself is unchanged.appanddbindocker-compose.yml, andappindocker-compose.sqlite.yml, now setcontainer_name(openmtg,openmtg-db) instead of Compose's default<project>-app-1naming.
-
Migration portability test suite -
backend/tests/test_migration_dialect_safety.pystatically scans every migration forop.alter_column/op.drop_columncalls outsidebatch_alter_table, which break on SQLite.backend/tests/test_migration_schema_parity.pyreplays the real Alembic chain against a fresh SQLite database and a fresh PostgreSQL database and diffs the resulting schemas.ci.yml- added apostgres:16-alpineservice container so the parity check runs on every push/PR.- Downgrading (
alembic downgrade) is not supported on SQLite - only the forwardupgrade headpath is exercised in production.
- Version bumped from 1.9.0 to 1.9.1 within
constants.py.
-
Loan Tracking - Track cards loaned out to other players from the Collection.
on_loan,loaned_to, andloan_datefields added toCollectionEntrymodel and schemas.- Loan section added to the Edit Card modal with an on-loan toggle, borrower name field, and loan date picker.
- "On Loan" badge shown on loaned entries in the Collection view.
- Alembic migration
f0a1b2c3d4e5adds the three new columns tocollection_entries.
-
Card Photos - Upload and view front and back condition photos for individual collection entries.
- Photos stored on disk at
/data/uploads/card_photos/. One photo per side per entry; re-uploading replaces the existing photo. Served through FastAPI (not nginx) so authentication is enforced on every request. card_photostable with cascade delete tied to the parent entry. Alembic migrationf1a2b3c4d5e6.POST/DELETE/GET /collection/{entry_id}/photos/{side}- upload, remove, and serve photos (owner only).PhotoUploadModalcomponent - drag-and-drop or file browse with live preview. Loads any existing photo for the selected side on open, functioning as both viewer and uploader.CardPhotoViewercomponent - Front/Back tab switcher that fetches photos as authenticated blob URLs viaURL.createObjectURL().- Updated CSP
img-srcinnginx.confto includeblob:forURL.createObjectURL()to render in the browser.
- Photos stored on disk at
-
Trade System - Formal trade proposals, negotiation, and automatic card transfers between accounts.
- Trades stored in a dedicated SQLite database (
/data/trades/trades.db) fully independent of the main PostgreSQL inventory. Deleting it removes all trade history without affecting card data. TradeandTradeItemmodels inbackend/models/trade.pyusing a separate SQLAlchemy engine and session (database_trades.py).- Separate Alembic environment (
alembic_trades.ini,migrations_trades/) for the trades database. - Trade state machine:
proposedtoactivetoaccepted/rejected/cancelled. On double-confirmation, cards transfer automatically, validated against live quantities before execution. GET /trades/pending-count,GET /trades,POST /trades,GET /trades/{id},PUT /trades/{id}/items,POST /trades/{id}/confirm,POST /trades/{id}/unconfirm,POST /trades/{id}/reject,GET /trades/{id}/photos/{entry_id}/{side}.Trades.jsx- trade list page with Active and History sections, status color badges, "Your Turn" indicator, and a Propose Trade modal.TradeDetail.jsx- split-screen view of both offers with live totals (condition multipliers applied from local state), and a Confirm / Un-submit / Reject action bar.TradeAddCardModal- pick cards from your collection to add to a trade, with a gold-outline selected state.!badge on the Trades nav link (desktop and mobile) when any trade is awaiting your action.services/webhooks.pystub included as the foundation for v1.10 Home Assistant integration.
- Trades stored in a dedicated SQLite database (
-
Trades Feature Toggle - Admins can enable or disable Trades from the Feature Toggles section of the Settings page.
GET /trades/statuspublic endpoint fetched byAuthContexton init; exposestradesEnabledto all components via context.- When disabled: the nav link is hidden, trade pages redirect to
/collection, and pending count returns zero. trades_enabledadded toservices/settings.pyDEFAULTS,SettingsUpdateschema, and thePATCH /admin/settingshandler.
-
Scryfall batch price refresh - Price updates now use
POST /cards/collectionto fetch up to 75 cards per request, replacing individualGET /cards/{id}calls.scryfall_queue.post()method added with a configurable per-request HTTP timeout (30 s for batch vs 10 s for single-card GETs).price_refresh.pyrewritten to batch all cards in groups of 75, mapping Scryfall responses back to database records byscryfall_idand committing per batch.
-
Multi-platform Docker image builds targeting
linux/amd64andlinux/arm64viadocker buildx.
docker-compose.ymlvolume paths for uploads and trades use inline shell defaults (${UPLOADS_PATH:-./uploads},${TRADES_PATH:-./trades}) so the stack starts without those variables set in.env.README.mdCredits section updated with a Scryfall attribution line.
- Added per-account login throttling in
backend/login_throttle.py. After 5 failed attempts within 10 minutes, the account enters a 5-minute cooldown. Returns the same generic 401 as a wrong-password response. - Added security response headers to
nginx.conf:X-Content-Type-Options: nosniff,X-Frame-Options: SAMEORIGIN,Referrer-Policy: strict-origin-when-cross-origin, and aContent-Security-Policylocking scripts to'self', images toself/data:/Scryfall CDN, and frames tonone. Users running TLS should add HSTS on their own reverse proxy. - Added minimum password length of 8 characters to
RegisterRequest,CreateUserRequest, andUpdateUserRequestinbackend/schemas.pyvia PydanticField(min_length=8). - Added test coverage for login throttle.
- Fixed a broken rate-limit key, uvicorn now starts with
--proxy-headers --forwarded-allow-ips=127.0.0.1insupervisord.conf, so slowapi'sget_remote_addressreads the real client IP from nginx'sX-Real-IP/X-Forwarded-Forheaders instead of always seeing127.0.0.1. - Fixed a TOCTOU race on the first-run
/auth/setupendpoint: the count-then-insert now catchesIntegrityErrorand rolls back, so two simultaneous requests cannot both create an admin account. - Renamed the Card Search feature from
scannertocard-searchthroughout the codebase. The API endpointGET /scanner/statusis nowGET /card-search/status. The settings keyscanner_enabledis nowcard_search_enabledinbackend/services/settings.py,backend/routers/settings.py,backend/schemas.py(SettingsUpdate), and the frontend Settings page. Frontend routes and nav links updated from/scannerto/card-search. A compatibility shim inservices/settings.pytranslates the old DB key on read for existing instances. Alembic migrationa9b8c7d6e5f4renames the row in the settings table. - Split
backend/models/__init__.pyinto one file per model group:user.py,card.py,collection.py,deck.py,setting.py,wishlist.py,currency.py,price_history.py.__init__.pyre-exports all classes, so all existingimport modelscall-sites are unchanged. - Updated
README.mdPython badge and tech-stack table from3.12+to3.14to match the Dockerfile. - Updated
backend/tests/test_adminandbackend/tests/test_authto new 8-character passwords for tests. - Fixed slowapi storage isolation between tests.
- Bumped
anyiodependency from>=4.14.0to>=4.14.1. - Fixed Alembic migration ordering.
- Clarified Deck Import Modal language and word formatting.
Added a module-level in-memory cache for application settings in backend/services/settings.py to reduce calls to the settings DB.
Added a limit(10000) cap to the collection GET endpoint in backend/routers/collections.py to reduce the maximum number of cards loaded at a time.
Added staleTime: Infinity to frontend/src/pages/Collection.jsx to reduce redundant full-collection calls on an HTML mutation unuless an actual change occurs.
Replaced PyJWT[crypto] with PyJWT backend/requirements.txt which did not use any RSA or elliptic-curve JWT algorithms.
Changed the price refresh cycle in backend/services/price_refresh.py to load only one card at a time into memory instead of the full collection.
Removed the tesseract.js dependency from frontend/package.json.
Removed asyncpg from backend/requirements.txt which was never imported or used.
Deleted frontend/nginx.conf, leftover and unused file.
Deleted nginx/nginx.conf, leftover and unused file.
-
Showroom - A public, unauthenticated display page for each user's collection highlights.
GET /showroom/display/{username}- Public endpoint returning the user's public decks and showroom-flagged collection cards. Username matching is case-insensitive.GET /showroom/display/{username}/deck/{deck_id}- Public endpoint returning the full card list for a shared deck./showroom/display/:username- Public Showroom display page showing decks with card preview strips and a card grid./showroom/display/:username/deck/:deckId- Public read-only deck viewer page with Commander, Main Deck, and Sideboard sections. All cards are clickable to enlarge./showroom/edit/:username- Owner-facing Showroom management page. Shows everything currently on display with per-item remove controls and a "View Display" link.- Per-deck showroom toggle on the Decks page (
Eyebutton) marks a deckis_publicand surfaces it on the owner's Showroom display. - Per-card showroom toggle on the Collection page (
Eyebutton, both desktop action row and mobile action menu) setsin_showroomon a collection entry. - Showroom navigation link (
Eyeicon) added to sidebar and mobile menu, conditionally shown based onshowroomEnabledfromAuthContext. - S/M/L card size selector on both Showroom pages, persisted per-browser via
usePersistedViewand shared between display and edit views. - Alembic migration
b2c3d4e5f6a7- addsin_showroomboolean column (defaultfalse) tocollection_entries. - New
in_showroomfield added toCollectionEntrymodel,CollectionEntryOutschema,UpdateCardRequestschema, and thePATCH /collection/{id}handler.
-
Deck Import - Paste a Moxfield, MTGO, or Arena deck list and create a populated deck in one step.
POST /decks/import- Streaming SSE endpoint. Creates the deck, then processes each card line and yieldsstart,progress(with card name), anddoneevents so the frontend can show real-time progress. Handles Moxfield set+number lookup with a name-based fallback. RecognisesCommander,Sideboard,Mainboard,Maindeck,Main, andDecksection headers. Commander entries are forced to quantity 1.DeckImportModalcomponent - Modal with deck name, format, and description fields plus a card list textarea. During import, the hint text is replaced by a real-time progress bar showingN of total - Card Name. Returns an imported/skipped summary with a per-line error list and a "View Deck" link on completion. Cancel is disabled while streaming.- Import button added to the Decks page header alongside the existing "New Deck" button.
-
Deck card preview strips - Horizontal scrolling strip of card images shown on every deck row.
DeckPreviewRowcomponent - Shared across the Showroom display, Showroom edit, and Decks list pages. Uses aResizeObserverto calculate exactly how many cards fit in the available width and slicespreview_cardsaccordingly. Commander cards are highlighted with an accent-color outline. Accepts an optionalactionsslot rendered after the strip.GET /decksnow eager-loads allDeckCard → Cardrelationships and manually buildspreview_cards(commanders first, then mainboard) andcard_countper deck.DeckOutschema extended withcard_count: int = 0andpreview_cards: list[DeckPreviewCard] = [](defaulted so PATCH responses remain valid).
-
Feature Toggles - Admin-controllable on/off switches for optional features, replacing the previous per-feature hardcoded visibility.
- Showroom toggle: disabling hides the nav link, the public display and deck-viewer pages return 404, and all per-card/per-deck eye buttons disappear.
- Card Search toggle: disabling hides the Card Search nav link and redirects any direct navigation to
/collection. GET /scanner/status- New public endpoint (mirrors/showroom/status) reporting whether Card Search is enabled.scanner_enabledandshowroom_enabledadded toservices/settings.pyDEFAULTS (both"true"),SettingsUpdateschema, and thePATCH /admin/settingshandler.AuthContextfetches both/showroom/statusand/scanner/statuson init and exposesshowroomEnabledandscannerEnabledvia context. Failures are non-fatal.
-
New Pydantic schemas:
DeckPreviewCard,DeckImportRequest,DeckImportResult,ShowroomPreviewCard,ShowroomDeckOut,ShowroomCardOut,ShowroomOut. -
New CSS: Showroom page layout, deck preview strip, card grid clickable state, deck viewer header, import progress bar, showroom card placeholder, and commander highlight styles.
-
New
README.mdbadges for Architecture, Scryfall, Last Commit + Release, and CI Pass/Fail status. -
Updated
nginx.confto proxy/openapi.jsonfor future API-based tooling.
- Settings page: removed the "Save Settings" button. All settings now auto-apply when changed, feature toggles fire immediately on toggle, and the price refresh slider saves on
mouseup/touchend(not on every drag tick). - Settings page: "Showroom" settings section renamed to "Feature Toggles" to accommodate multiple toggleable features.
- Decks list: rows now use
DeckPreviewRow(card image strip + info) instead of the previous plain name/format text layout. - Version bumped from 1.7.0 to 1.8.0 within
constants.py. - Redirected
dependabot.ymlto dev branch instead of main. - Corrected Mobile view of the User Management page, content now split between multiple rows for each user.
- Multi-currency support - Admins can now add custom currencies (e.g. CAD, AUD, GBP) via the Admin panel. Rates are fetched automatically from Frankfurter and refreshed after each Scryfall price cycle. Admins can select any configured currency from the User Account settings for any user.
backend/markets.py- Central currency registry allowing each market to define its symbol, display name, Scryfall adapter, and capabilities in one place.backend/services/market_scryfall.py- Scryfall price adapter which maps Scryfall API price keys to database column names.backend/services/exchange_rates.py- Frankfurter integration for validating and batch-refreshing stored exchange rates.GET /currencies- Public endpoint; frontend fetches all currency metadata (symbol, rate, conversion base) at runtime instead of hardcoding.GET|POST|PATCH|DELETE /admin/currencies- Admin CRUD for custom currencies. New codes are validated against Frankfurter before being accepted.useCurrency()hook - Replaces scattereduser?.preferred_currencyreads across all pages; providescurrency,market, andmarketsto any component that needs them.ConvertedCurrencydatabase model and Alembic migration.- New 'SM', 'MD', and 'LG' buttons to Grid views for Decks and Wishlist which changes the visible card size.
- Price extraction in
scryfall.pyandprice_refresh.pynow delegates to the market adapter (ScryfallMarket.extract_prices()), eliminating all hardcodedif prices.get("usd")chains. currency.js-formatPriceandresolvePriceare now market-aware. Custom currencies apply a stored exchange rate against USD automatically on the frontend.- Collection stats endpoint now supports custom currencies via DB rate lookup. All price expressions are multiplied by the exchange rate server-side.
- Wishlist price history response is now dynamic across all markets rather than hardcoded to USD/EUR fields.
set_currencyin auth now validates the chosen code againstMARKETSand the database before accepting it.PRICE_FIELDSconstant removed fromconstants.py. All callers now derive field names fromMARKETS.- All JSX inline styles with more than one property moved to named CSS classes in
index.css. Dynamic values are passed via CSS custom properties (--bar-w,--bar-bg,--tile-accent).
add_to_wishlistendpoint was calling_serialize(entry, currency)after_serializesignature was updated to take one argument, causing a 500 on all POST/wishlistrequests.
- Server-side
price_metcomputation removed from wishlist serializer, field is now computed on the frontend where currency context is available. - Removed CSS class
.wishlist-pagelimiting Wishlist width to a specific pixel count; Wishlist is now adopts full screen width.
- Deleting a user now correctly removes their collection, deck, and wishlist entries; previously caused a 500 error due to missing cascade delete on the User-CollectionEntry, User-Deck, and User-WishlistEntry relationships
- Mobile navigation replaced with a hamburger menu (☰) in the top-right; tapping it opens a full-width dropdown with page names and Logout at the bottom.
- Long usernames no longer push nav items off-screen, as usernames are no longer rendered in Mobile view.
- User Management table unified for mobile and desktop; Less useful Email and Created columns are hidden on narrow screens rather than switching to a separate card layout
- Collections: page GOTO input replaces static page indicator - type a page number and press Enter to jump directly
- Wishlist list view on mobile: two-row card layout (name + price on top, set code + action buttons below); set name abbreviated to 3-letter code; History button restored
- User Management on mobile: per-user cards with a status row (name, role, status) and an action row (currency, admin toggle, reset password, disable, delete); desktop table unchanged
- Stats Top 10 Most Valuable Cards on mobile: two-row card list instead of the overflowing table
- List/Grid toggle order standardized to List first across all pages (Decks and Wishlist)
- Deck and Wishlist List/Grid view preference is now persisted per-browser; each deck remembers its own setting independently
- Deck view total and per-card prices now respect the user's preferred currency instead of always showing USD
- Stats page loading spinner used incorrect CSS class (
isLoading→loading)
- Stats: removed local duplicate of
formatPriceandCURRENCY_SYMBOLSin favour of the sharedcurrency.jsutility
- Added grid view to Deck Viewer (default), with card images, quantity badges, and hover actions
- Added card image viewer to Deck Viewer (both grid and list views)
- Overhauled "Add Card to Deck"; card image thumbnails in search results, owned/non-owned toggle, zone dropdown (Mainboard/Sideboard/Commander), and set picker for non-owned cards
- Added "Edit Card" modal to Deck Viewer
- Added Deck Analysis including Mana Curve, Color Distribution, Card Types, Avg. CMC, and Rarity breakdown
- Two-step delete confirmation on card removal in Decks to prevent accidental deletes
- Updated API/Application version handling in all background files involving API calls
- Added Wishlist page
- Added List and Grid views to Wishlist page
- Pushed all API calls to a single rate-limited caller function to enforce Scryfall's 2 req/sec rate cap
- Set up API call prioritizer to push frontend user activity through API caller function first
- Deck Edit modal now supports changing a card's printing via Set Picker
- Wishlist cards are prioritized in background price cache refresh
- Unified Add Card button sizes across Wishlist, Decks, and Deck Detail pages
- Fixed deck card update endpoint returning a 500 instead of 404 on an invalid Scryfall ID
- Fixed Admin page delete confirmation using browser native dialog instead of the app's modal
- Set Picker dropdown now renders over modals using fixed positioning rather than being clipped
- Bump eslint from 9.39.4 to 10.3.0 in /frontend
- Update pytest-mock requirement from >=3.14 to >=3.15.1 in /backend
- Update pytest requirement from >=8.0 to >=9.0.3 in /backend
- Update anyio requirement from >=4.0 to >=4.13.0 in /backend
- Update httpx requirement from >=0.27 to >=0.28.1 in /backend
- Bump @eslint/js from 9.39.4 to 10.0.1 in /frontend
- Corrected use of
_HEARTBEAT_JITTERto the correct_HEARTBEAT_INTERVALfor telemetry timing. - Admin panel now shows a per-user currency dropdown that takes effect immediately without a page reload.
- Scryfall service now fetches and stores all four price fields:
price_usd,price_usd_foil,price_eur,price_eur_foil. - Currency selection is driven by a
PRICE_FIELDSregistry inconstants.py, making future currencies (e.g. CAD) a one-line addition.
- Added a check to see date of creation for current UUID, and re-generate UUID if >60 days.
- Added a check for timestamp of last message compated to current message, and delay heartbeat by an hour if within 23 hours of previous heartbeat.
- Added a dropdown in the Settings menu next to the Telemetry toggle to see the last-sent telemetry packet in its entirety.
- Added a data retention statement in the README.md and Wiki.
- Lowered timestamp accuracy to round to the nearest minute.
- Replaced invisible Telemetry tab with disabled message when
NOTEL=trueis set.
- Corrected duplicated 'Uvicorn' processes in 'supervisord.conf'
- Added optional usage telemetry to Settings page (Opt-in only, see README.md)
- Corrected missing icons from mobile web view
- Updated eslint/js from 9.39.4 to 10.0.1
- Updated lucide-react from 0.577.0 to 1.7.0
- Modified
httpxusage inprice_refresh.pyandscryfall.pyto use existing HTTP handshake instead of creating a new one for every card requests. DNS requests forapi.scryfall.comshould fall dramatically now. - Updated all
utcnow()calls to propernow(timezone.utc)calls. - Fixed SQLite thread safety and suppressed test scheduler startup noise in
conftest.pyanddatabase.py.
- Removed known remainder of AI code. Repository has been cleaned and is now 100% human-developed. Summary of major changes below
- Removed unused Search icon and SetPicker import.
- Properly split components
AddCardModal,EditModal, andCardImageModalinto imported components. - Replaced complicated
const onMobile = /Mobile/i.test(navigator.userAgent)with simplerconst isMobile = useIsMobile()hook. - All
onMobilereferences changed toisMobile. - Replaced all outdated
window.confirm()calls with propersetConfirmAction({ message, onConfirm })calls. - Replaced color filter logic
getCardCastingColors(entry.card)with(entry.card.colors || '').split('')to use the colors string already stored from Scryfall API cache instead of parsingmana_costin frontend every time.
- Replaced
const isMobile = /Mobile/i.test(navigator.userAgent)withimport { useIsMobile } from '../hooks/useIsMobile'. - Added
const isMobile = useIsMobile()inside the component body, for dynamic pointer type changes.
- Placeholder UI template has been removed. Dev-intended UI is now in place.
- Changed ruling link from Gatherer to Scryfall.
- Added backend tests.
- Added clickable card images in Collection which blows the card image to full size, and provides a link to the
gatherer.wizards.comruling for that card. - Added multi-card selection for batch deleting from Collection.
- Improved the mobile webpage rendering.
- Re-ordered and improved Collection filters.
- Implemented adding and sorting cards by 'Favorite'.
- Combined Docker images
openmtg-backend,openmtg-frontend, andnginxinto a single Docker imageopenmtg. - Modified how Stats page shows pie charts to help with rendering small percentages.
- Added CHANGELOG.md.
- Added CREDITS.md.
- Edited Collection page to use pagination through a drop-down menu.
- Corrected CSV and JSON export functions.
- Fixed Deck building page occasionally not working.
- Added Deck Moxfield and JSON export buttons.
- Added Sorting and Filtering features to Collection page.
- Corrected tab names to reflect which tab the user is on, as well as the project name.
- Added new Favicon, credited to Faithtoken and licensed under CC BY 3.0.
- Updated 'Database Cache Freshness Bar' to make it a live updating element instead of a static one.
- Edited
frontend/Dockerfileto addRUN apk upgrade --no-cache, clearing known libexpat and zlib CVE's. - Edited
backend/Dockerfileto addRUN apt-get update && apt-get upgrade -y && apt-get clean && rm -rf /var/lib/apt/lists/* && pip install --upgrade pip, clearing CVE-2025-8869. - Created
nginx/Dockerfileto build nginx instead of pulling image. - Replaced
ecdsawithPyJWTinsecurity.py, clearing ecdsa CVE-2024-23342.