Technic Solder is an API that sits between a modpack repository and the launcher. It allows you to easily manage multiple modpacks in one single location. It's the same API we use to distribute our modpacks!
Using Solder also means your packs will download each mod individually. This means the launcher can check MD5's against each version of a mod and if it hasn't changed, use the cached version of the mod instead. What does this mean? Small incremental updates to your modpack doesn't mean redownloading the whole thing every time!
-- Technic
solder.py is a Minecraft modpack management platform designed to remain compatible with Technic Solder while adding easier management and distribution options for other launchers. It began as an upload interface for the Terralization modpack and grew into a complete standalone Solder implementation.
Administrators moving from the original project should start with solder.py for Technic Solder users. The mod and modpack management guide explains the fields and day-to-day workflow in the management interface.
The complete read API, including Technic-compatible routes and solder.py server and optional-manifest extensions, is documented in the API reference. Developers building a Solder-aware bootstrap mod should also use the dedicated bootstrap API guide, which covers advanced optional groups, dependency resolution, updates, and safe archive ownership.
Management-side Modrinth, GitHub config, and Maven imports are documented in the integration guide.
SolderPy Modpack Loader, MCInstance Loader, Packwiz, FileDirector, Modrinth, CurseForge, and Prism setup, export usage, and file layouts are documented in the distribution usage and format guide.
-
Easy install with docker
-
Efficient user experience
solder.py is designed to allow a minimal button clicking as possible.
-
Selected build feature
This feature allows the user to select a modpack build that the user is working on. menu has a own pin for it, new uploaded modversion can be added/updated to selected build directly and more.
-
Pin your modpacks to menu
-
Clone builds from other modpacks
-
-
Mod uploading
-
S3/R2 bucket compatibility
-
Required mod dependencies
Configure one or more dependencies on a mod's version page. Adding that mod to a build also adds the newest matching dependency version, including transitive dependencies, while preserving versions already in the build.
-
Modrinth integration
Search Modrinth from the management interface and link a project to the mod library without downloading every release. Selecting a compatible version in a build downloads, verifies and packages it on demand. Required Modrinth dependencies are imported recursively, recorded in Solder's dependency list, and added to the build. Modrinth does not require an API key.
-
GitHub config-pack integration
Link a public GitHub repository to an existing
CONFIGentry. Synchronize a manually versioned branch, tag, or commit, or lazily import immutable repository tags from the build editor. Repository metadata is removed, pinned GitHub submodules are included, and the result is stored as a normal Solder ZIP. -
Universal Maven integration
Configure a standard Maven repository, group ID, artifact ID and optional classifier. solder.py reads
maven-metadata.xml, maps upstream releases to Minecraft versions, and downloads only the release selected for a build. -
Reviewed integration manifest import and export
Import JSON resolved to Modrinth project IDs or exact Maven coordinates, with optional name, description, author, link, side, modloader, and Minecraft mapping metadata. Export configured integrations to the same portable format for review or transfer to another installation.
-
-
API only mode
Run a public read API with read-only database permissions while keeping a separate management instance on a private network.
-
Shadow builds
A shadow build is generated from an existing build using its side and optional metadata.
-
Optional shadow build
Mark a mod optional per build, useful for additions such as shaders or higher-demand client features.
-
Advanced optional groups
Give SolderPy Modpack Loader, MCIL, and FileDirector choices names, independent or exact-one selection rules, defaults, and a Technic/basic state of Required, Optional, or Excluded. Public builds can optionally bootstrap SolderPy Modpack Loader through a normal Technic Solder package so the same choices are available before mod discovery.
-
Server shadow build
Mark mods for the client, server, or both and expose a server-compatible view of the same build. Export a small dedicated-server ZIP containing SolderPy Modpack Loader and Relauncher in
mods/plus one verified server-sideLAUNCHERJAR, such as Crucible, at the archive root; the remaining server content is installed and updated from the bootstrap API.
-
-
Internal notation on mods
-
Generate changelog
-
MCInstanceLoader export support
Export a build from its management page as an MCInstanceLoader
.mcinstancearchive. -
SolderPy Modpack Loader, Packwiz, and FileDirector support
Export a SolderPy Modpack Loader configuration ZIP, or let Technic, Modrinth, CurseForge, and Prism deliver the Loader and Relauncher while using the same dedicated API. Packwiz metadata and FileDirector bundles remain available as separate formats. Each format is independently enabled in the settings GUI, and the hosted routes are available in API-only mode.
-
Modrinth and CurseForge pack exports
Export a build as an MRPack or CurseForge archive. Modrinth-mapped versions remain native in MRPack; other packages are installed from Solder through SolderPy Modpack Loader, MCInstance Loader, or FileDirector where available. CurseForge packs contain only the selected downloader and any enabled Modrinth-CurseForge sync mappings as native projects. Hybrid combines native platform downloads with a downloader for the remaining packages. Solder API only leaves ordinary build packages to the downloader; with SolderPy Modpack Loader, its API can offer verified override, Modrinth/Maven, and Solder fallback URLs. The export window lets you choose a compatible downloader release loaded from the target platform API and temporarily override the exported Forge/modloader version without changing the saved build.
-
Prism Launcher instance exports
Export a directly importable Prism/MultiMC instance containing
mmc-pack.jsonandinstance.cfg. Use a verified SolderPy Modpack Loader, MCIL, or FileDirector bootstrap for a small archive, or use the self-contained fallback when the build cannot use a downloader. Minecraft and the modloader remain launcher-managed. -
Database compatibility with Technic Solder
solder.py preserves the core Technic tables and adds the fields and tables needed for its additional features.
Open Browse mods in the management menu to search and add provider-managed mods. No project file is downloaded at this stage. In a modpack build, select the linked mod and then a compatible provider version. solder.py downloads the upstream JAR, verifies the provider hash and file size, packages the JAR as a normal Solder ZIP, and records the local version. Re-selecting it reuses the stored version. Required Modrinth projects are imported recursively and linked through the normal Solder dependency list. Exact dependency versions are used when Modrinth supplies them; otherwise solder.py selects the newest compatible release.
See the integration guide for storage, permissions and operational details.
An existing local mod with a different slug can be linked manually from its management page using a Modrinth URL, slug, or project ID. This keeps its local slug, versions, and build assignments.
Set an existing mod's type to CONFIG, save it, and use Link GitHub on its
management page. A manual sync accepts a branch, tag, or commit plus an explicit
version. Tagged repositories also expose their tags in the build editor and to
Update all mods, so only the selected immutable tag is downloaded and
packaged. This integration reuses the existing provider columns and adds no
GitHub-specific database tables.
Repositories can include a .solderpyignore file to omit development,
server-only, or generated paths from the packaged config ZIP. It supports
gitignore-style wildcards, root-relative rules, comments, and ! re-inclusion.
See the integration guide for archive filtering, submodule handling, limits, and credentials.
Open Maven in the management menu and add the repository base URL first.
Then add a mod using its group ID, artifact ID, optional classifier, and JAR
extension. Maven implementations only need to expose the standard repository
layout and maven-metadata.xml; solder.py does not require an Artifactory,
Nexus, or other vendor-specific API.
Each artifact has one explicit Minecraft mapping mode:
- Contained in Maven version uses a format such as
{minecraft}-{version}. For example,1.7.10-9.10.48maps to Minecraft1.7.10and mod version9.10.48. - Always one Minecraft version assigns every upstream release to the fixed Minecraft version selected by the administrator.
- Manual per version leaves new releases disabled until their Minecraft and mod versions are entered on the artifact page.
Refreshes preserve manual corrections and mark releases removed from upstream metadata as unavailable. The build editor and Update all mods only consider available, enabled releases matching the build's Minecraft version and modloader. Listing or refreshing releases downloads metadata only. The selected JAR is downloaded, checksum-verified when Maven publishes a standard checksum sidecar, validated as a JAR, and packaged into the normal Solder repository. Timestamped Maven snapshots are resolved through their version-level metadata. On an artifact's management page, Use the Maven JAR URL directly lets SolderPy Modpack Loader download that exact native Maven artifact before falling back to the Solder-hosted JAR. This is opt-in and requires a public HTTPS Maven repository. The downloaded bytes are always checked against the raw-JAR MD5 stored when solder.py imported the version.
See the integration guide for the complete workflow.
Enable the required formats under Settings > Env Settings, then open a build's Export window. The complete distribution usage and format guide explains the shared Solder API only and hybrid options, SolderPy Modpack Loader and MCInstance Loader archive mapping, Packwiz and FileDirector hosting, Modrinth or CurseForge fallback exports, and Prism Launcher bootstrap or self-contained instance exports.
Direct publishing is configured under Settings > Publishing. Add a
Modrinth personal access token or CurseForge author API token, map a
local modpack to an existing remote project, and use Publish in the build's
Export window. solder.py generates the same archive offered by the normal
download button, uploads it as a new remote version, and records the result.
If that live build is changed after a successful publication, the action becomes
Send patch and creates -Patch-1, -Patch-2, and later remote versions.
Publishing accounts and mappings belong to the solder.py user who created them;
another user who manages the same modpack must configure their own upload token
and mapping.
solder.py keeps the Technic read API paths and response defaults. A normal launcher request remains unchanged:
GET /api/modpack/example-pack/1.0
Clients that understand solder.py's shadow-build features can request a target and optional packages with query arguments:
GET /api/modpack/example-pack/1.0?target=server
GET /api/modpack/example-pack/1.0?optional=true
GET /api/modpack/example-pack/1.0?target=server&optional=true
target=client includes CLIENT and BOTH packages. target=server includes
SERVER and BOTH packages. Optional packages are excluded unless
optional=true is supplied. The existing -optional and -server build
suffixes remain supported as aliases.
Server updaters can use recommended or latest in place of a build version.
Extended manifests report the resolved version, target, optional mode and a
stable SHA-256 manifest hash. Their mod entries also report side, modtype,
build-specific optional status and declared dependencies:
GET /api/modpack/example-pack/recommended?target=server
Supplying an installed build in from also returns package-level additions,
updates and removals:
GET /api/modpack/example-pack/latest?target=server&from=1.0
The comparison describes Solder packages. A server updater remains responsible for tracking which files were extracted from each package when removing an old package.
The supported installation is Docker Compose with MySQL and a reverse proxy. The example below also gives the mod repository its own web server: Caddy serves ZIPs and JARs directly while proxying solder.py to Gunicorn. This avoids serving large mod files through Python and does not require S3 or R2.
Before starting, you need:
- a Linux host with Docker Engine and the Compose plugin;
- a domain pointing to the host;
- inbound TCP ports 80 and 443;
- persistent storage for MySQL, Caddy, and the mod repository;
- current backups of the database and repository when upgrading or migrating.
The application container must not be exposed directly to the internet. Use Caddy, NGINX, or another trusted reverse proxy for HTTPS. The Compose example keeps both MySQL and Gunicorn on private Docker networks.
Useful follow-up documentation:
- solder.py for Technic Solder users
- mod and modpack management guide
- GitHub config repository guide
- distribution usage and format guide
- dedicated Solder bootstrap API guide
- environment variable template
Modrinth and CurseForge archives can also include administrator-configured native bootstrap projects. Manage these under Settings > Modrinth-CurseForge sync; TX Loader is supplied as a disabled default mapping. See the distribution guide for how sync mappings interact with Solder API only and hybrid exports.
If you do not want to use S3 or R2, the recommended Docker setup is to let
Caddy serve the local mod repository and
reverse proxy solder.py. Caddy serves large files below /mods/ directly and
proxies the dynamic site, API, Packwiz metadata, and Director manifests to
Gunicorn. Only Caddy is exposed to the internet. The solder.py container writes
files to the shared repository while Caddy mounts that repository read-only;
MySQL is reachable only on a private Docker network.
This example uses solder.example.com. Before starting it:
- Point the domain's DNS record at the Docker host.
- Allow inbound TCP ports 80 and 443. UDP port 443 is optional and enables HTTP/3.
- Create the repository directory and make sure the user running the solder.py container can write to it:
sudo mkdir -p /srv/solder/modsCreate a file named Caddyfile beside the Compose file:
solder.example.com {
encode zstd gzip
# handle_path removes /mods before looking in /srv/solder-mods. A request
# for /mods/example/example-1.0.zip therefore maps to
# /srv/solder-mods/example/example-1.0.zip.
handle_path /mods/* {
root * /srv/solder-mods
file_server
}
# These files are generated from the selected build. Keep the full path
# and proxy them to solder.py instead of looking for them under /mods.
handle /packwiz/* {
reverse_proxy solderpy:5000
}
handle /filedirector/* {
reverse_proxy solderpy:5000
}
# Management pages and the Technic-compatible API.
handle {
reverse_proxy solderpy:5000
}
}Directory browsing is not enabled, so requests to repository directories do not produce file listings.
Create a Compose .env file beside compose.yml. Use different, randomly
generated values in production and do not commit this file:
SOLDER_IMAGE=thorfusion/solderpy:1.10.2
SOLDER_DB_PASSWORD=replace-with-a-long-random-password
MYSQL_ROOT_PASSWORD=replace-with-another-long-random-password
SOLDER_SECRET_KEY=replace-with-a-long-random-application-secret
# Optional; enables automatic CurseForge matching and downloader lookup.
CURSEFORGE_API_KEY=Then create compose.yml:
services:
mysql:
image: mysql:8.4
restart: unless-stopped
environment:
MYSQL_DATABASE: solderpy
MYSQL_USER: solderpy
MYSQL_PASSWORD: ${SOLDER_DB_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p\"$$MYSQL_ROOT_PASSWORD\" --silent"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
networks:
- solder_database
solderpy:
image: ${SOLDER_IMAGE}
restart: unless-stopped
depends_on:
mysql:
condition: service_healthy
expose:
- "5000"
environment:
APP_URL: https://solder.example.com/
SECRET_KEY: ${SOLDER_SECRET_KEY}
DB_HOST: mysql
DB_PORT: "3306"
DB_USER: solderpy
DB_PASSWORD: ${SOLDER_DB_PASSWORD}
DB_DATABASE: solderpy
PUBLIC_REPO_LOCATION: https://solder.example.com/mods/
MD5_REPO_LOCATION: /app/mods/
# Required only when CurseForge archive exports are enabled.
CURSEFORGE_API_KEY: ${CURSEFORGE_API_KEY:-}
# Caddy has a fixed address so only it is trusted as the reverse proxy.
PROXY_IP: 172.30.50.2
volumes:
- /srv/solder/mods:/app/mods
networks:
solder_frontend:
ipv4_address: 172.30.50.3
solder_database:
caddy:
image: caddy:2-alpine
restart: unless-stopped
depends_on:
- solderpy
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- /srv/solder/mods:/srv/solder-mods:ro
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
solder_frontend:
ipv4_address: 172.30.50.2
networks:
solder_frontend:
ipam:
config:
- subnet: 172.30.50.0/24
solder_database:
internal: true
volumes:
mysql_data:
caddy_data:
caddy_config:Neither MySQL's port 3306 nor solder.py's port 5000 is published. Caddy is
the only public entry point. If 172.30.50.0/24 overlaps an existing Docker or
LAN network, choose another private subnet and update both fixed addresses and
PROXY_IP.
Start or update the installation with:
docker compose pull
docker compose up -d
docker compose psCaddy automatically obtains and renews HTTPS certificates when the domain
resolves to the server and ports 80 and 443 are reachable. The caddy_data
volume must remain persistent because it stores Caddy's certificates and other
state. On a new installation, open https://solder.example.com/setup and
create the first administrator after the containers become healthy.
If startup fails, inspect only the application and database logs first:
docker compose logs --tail=200 solderpy mysqlWith this layout, the important solder.py repository settings are:
PUBLIC_REPO_LOCATION=https://solder.example.com/mods/
MD5_REPO_LOCATION=/app/mods/
APP_URL=https://solder.example.com/
SECRET_KEY=replace-with-a-long-random-application-secret
# Used for automatic CurseForge file and downloader-version lookup.
CURSEFORGE_API_KEY=PUBLIC_REPO_LOCATION is the URL used by launchers. MD5_REPO_LOCATION is the
local path used by solder.py for hashing and file inspection, so those internal
operations do not need to download the file again through Caddy. APP_URL is
the trusted public base URL used in generated FileDirector remote pointers.
SECRET_KEY signs management login cookies and must remain private and stable
across restarts and replicas.
CURSEFORGE_API_KEY is a CurseForge third-party API key used only to list and
verify files while generating CurseForge archives. A manually entered
CurseForge file ID on a synced Modrinth version bypasses that version's lookup;
automatic matching and downloader-version lookup still require the key.
Writable schema repair also generates one ECDSA bootstrap-manifest signing key
for the entire solder.py installation and stores it in solder_settings. The
private key is never returned by the API or included in an export. SolderPy
Modpack Loader exports contain only its public key. Preserve the database in
backups: losing or replacing this key requires recreating existing exports so
clients can pin the replacement public key.
All distribution formats are disabled by default. After setup, open **Settings
Env Settings**, enable the formats that this installation should publish or export, and save. Packwiz and FileDirector files are still served when the solder.py container has
API_ONLY=True; the enable switches live in MySQL so management and API-only containers can share them. MRPack and CurseForge archives are downloaded from the authenticated build editor.
After uploading a mod version, verify both services through Caddy:
curl -I https://solder.example.com/api/
curl -I https://solder.example.com/mods/example-mod/example-mod-1.0.zip
curl -I https://solder.example.com/packwiz/example-pack/latest/pack.toml
curl -I https://solder.example.com/filedirector/example-pack/latest/mods.bundle.jsonReplace the example slugs with a public pack and uploaded mod from the
installation. Back up /srv/solder/mods and take regular logical MySQL backups;
the mysql_data volume, repository, and caddy_data volume all persist across
container replacement. The mod repository is user data and is not stored inside
either Docker image.
Back up MySQL and the repository first. Change SOLDER_IMAGE to the release you
want, then recreate only the services whose image or configuration changed:
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=200 solderpyThe management process repairs the database against the current solder.py schema at startup; it does not run a chain of per-release migrations. Its database user needs permission to create tables and alter columns/indexes. If repair fails, management login is not exposed and the error is logged. Do not start a read-only API service against a database until the management service has completed successfully. Keep the previous image tag in your deployment notes; a database backup is still required because rolling an application image back does not roll its schema or stored data back.
Back up the Technic database and repository, then start one management instance
with TECHNIC_MIGRATION=True. Its database user needs permission to create and
alter tables. Wait for technic database migrated! in the application log,
verify the API and a package download, then remove the setting and restart.
If the imported database has no solder.py administrator, temporarily set
NEW_USER=True, open /setup, create the account, and immediately remove the
setting. Start a separate API_ONLY=True service only after this migration is
complete. The full checklist is in the
Technic Solder migration guide.
The Compose example above already supplies the required values. Use this section when adapting it to an external database, S3/R2, or a split API and management deployment. The complete template is .env.example.
Running directly with Python has limited support. Use the locked Pipenv dependencies and a production WSGI server; the official image uses Gunicorn.
DB_HOST=mysql
DB_PORT=3306
DB_USER=solderpy
DB_PASSWORD=replace-with-a-long-random-password
DB_DATABASE=solderpyDB_HOST is the MySQL hostname as seen from the application container. In the
Compose example it is the service name mysql, not 127.0.0.1.
APP_URL is solder.py's trusted public base URL. Hosted FileDirector exports
use it instead of deriving a URL from the incoming request:
APP_URL=https://solder.example.com/Set a stable random secret for management login sessions. Changing it logs out all signed-in users:
SECRET_KEY=replace-with-a-long-random-application-secret
SESSION_COOKIE_SECURE=TrueManagement session cookies are HTTPS-only by default. Set
SESSION_COOKIE_SECURE=False only when developing locally over plain HTTP;
never disable it on a public deployment.
PUBLIC_REPO_LOCATION is the HTTP(S) prefix written into launcher manifests.
It must be reachable by players and should include its trailing slash:
PUBLIC_REPO_LOCATION=https://solder.example.com/mods/This is the internal repository source solder.py uses to calculate MD5 hashes and file sizes when rehashing or adding a mod manually. It is separate from the public launcher URL and accepts either:
- A direct HTTP(S) repository URL. Redirects are not followed, so configure the final URL.
- An absolute local repository path, which avoids an HTTP request and is faster. Inside the Docker image, use the container path (normally
/app/mods/), not the host-side volume path.
Both forms must point to the repository root containing <mod-slug>/<mod-slug>-<version>.zip.
MD5_REPO_LOCATION=https://solder.example.com/mods/or:
MD5_REPO_LOCATION=/app/mods/Public GitHub config repositories work without credentials. Installations that frequently browse or synchronize repository tags can set an optional token to raise GitHub's API rate limit:
GITHUB_TOKEN=Private GitHub repositories are not imported because Solder repository files are intended for public launcher distribution.
solder.py writes local artifacts under /app/mods even when object storage is
configured. Mount that path to persistent storage and let Caddy or another file
server expose it. Without a volume, the repository is lost with the container.
-v /your/path/here:/app/modsCACHE_TTL=300
CACHE_SIZE=100CACHE_TTL is measured in seconds. CACHE_SIZE is the maximum number of
responses retained by each in-process cache.
R2_ENDPOINT=
R2_URL=https://files.example.com/
R2_BUCKET=
R2_REGION=
R2_ACCESS_KEY=
R2_SECRET_KEY=Object storage is enabled when R2_BUCKET is set. Keep credentials in the
deployment .env, never in compose.yml or source control.
solder.py trusts one reverse proxy address for forwarded client IPs. Use the proxy's address as seen by the application container:
PROXY_IP=172.30.50.2Do not trust an arbitrary public range. The fixed private address in the Compose example keeps this boundary explicit.
NEW_USER=True temporarily re-enables /setup for an existing database.
Disable it immediately after creating the required account.
NEW_USER=TrueEnable this while starting a management instance against a Technic Solder
database. The current Technic schema is detected and the solder.py columns and
tables are added without removing or rewriting Technic fields. Migration runs
once during application startup; /setup no longer performs database changes.
TECHNIC_MIGRATION=TrueThe old form for registering a repository ZIP by version and MD5 is hidden by default. Enable it only when an installation still uses that manual workflow; normal uploads and provider imports are unaffected:
ENABLE_LEGACY_MODVERSION_ADDING=TrueAPI_ONLY=True exposes the read API and public distribution files without the
login or management interface. It is suitable for a public service using
read-only database credentials.
API_ONLY=TrueMANAGEMENT_ONLY=True exposes the management side without the read API.
MANAGEMENT_ONLY=TrueThe authenticated write API is disabled by default. Enable it only on the trusted management deployment, such as the instance behind your VPN:
WRITE_API=TrueCreate the initial bearer token from Settings > API Tokens after logging
in. Tokens use the owner's existing solder.py permissions and modpack access.
API_ONLY=True remains read-only unless WRITE_API=True is also set; an API
instance with writes enabled requires a database user with write permissions.
See the write API reference for routes and examples.
For a clean database, wait until MySQL and solder.py are healthy, then open
https://solder.example.com/setup. Enter the first administrator's email and a
strong password. After submission, sign in through /login.
If /setup reports a database error, do not keep retrying account creation.
Check docker compose logs solderpy mysql, correct the database connection,
and restart the services. On an existing database, /setup is closed when a
user already exists unless NEW_USER=True was deliberately enabled.
After the first login:
- Verify the public URL and repository paths under Settings > Env Settings.
- Enable only the distribution formats that will be used.
- Upload a small test mod and verify its ZIP through the public
/mods/URL. - Create an unpublished private test build and verify it in management.
- Publish a test build, verify its exact API route, and then set the desired
latestandrecommendedchannels. - Configure scheduled database and repository backups.
python -m pip install pipenv
python -m pipenv install
python -m pipenv run app
python -m pipenv lockThe test suite uses Python's standard library and does not need a live MySQL database or object-storage account. Install the locked application dependencies, then run all tests:
python -m pip install pipenv
python -m pipenv sync
python -m pipenv run python -m unittest discover -s tests -vThe production container and both MySQL fixtures can be tested locally with:
docker build --tag solderpy:database-test .
python -m pipenv run python tests/database_container_smoke.py solderpy:database-testThe checked-in SQL fixtures contain only table definitions and synthetic ci-*
records. The original populated dumps remain ignored and must not be committed.
GitHub Actions also verifies the lock file, imports/compiles the Python sources, checks the installed dependency set, and runs the same suite on every pull request and trusted branch push.
The weekly dependency updater needs a repository secret named
DEPENDENCY_UPDATE_TOKEN. Use a fine-grained token from an automation account
with access only to this repository and only Contents: read/write and
Pull requests: read/write. This separate token ensures the updater's pull
request triggers the required checks; the workflow never approves or merges it.
To prevent an untested pull request from being merged, add a branch rule for
main, enable Require status checks to pass before merging, and select the
solder.py tests check. The workflow also supports GitHub's merge queue.
New and changed management passwords use Argon2id with a random salt embedded
in the encoded value stored in users.password. Existing solder.py BLAKE2 and
passwords remain usable and are replaced with Argon2id automatically after
their next successful login. No password or salt schema migration is required.
Pull requests and trusted branch pushes run several complementary checks:
- CodeQL scans the Python and JavaScript sources with extended security queries.
- Bandit checks Python-specific security problems.
- pip-audit checks every locked production dependency for known vulnerabilities.
- Trivy checks the built production image for fixable high and critical operating
system and Python-package vulnerabilities, and publishes SARIF results to the
repository Security tab. The same workflow boots the image and verifies the
/api/health endpoint before the scan. - Container integration tests restore sanitized, synthetic versions of both a Technic Solder v1.3.1 database and a solder.py 1.7.4 database. They verify the Technic migration twice for idempotency, create a fresh database, and prove API-only startup works with read-only database credentials. They then exercise API keys, clients, modpacks, builds, and mod versions against a real MySQL server. The API checks cover public, hidden, private, unpublished, optional, server, and hyphenated build cases.
- GitHub dependency review checks dependency changes made by pull requests.
Run the Python checks locally with:
python -m pipenv sync --dev
python -m pipenv run python audit_dependencies.py
python -m pipenv run bandit --recursive app.py api.py alogin.py asetup.py asite.py models --severity-level medium --confidence-level mediumThe Python security and Container security checks can also be selected as
required status checks in the main branch rule.