Commit Graph
1086 Commits
Author SHA1 Message Date
dependabot[bot] 29a967a6e3 build(deps): bump bcrypt from 4.2.1 to 5.0.0 in /backend
Bumps [bcrypt](https://github.com/pyca/bcrypt) from 4.2.1 to 5.0.0.
- [Changelog](https://github.com/pyca/bcrypt/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/bcrypt/compare/4.2.1...5.0.0)

---
updated-dependencies:
- dependency-name: bcrypt
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-03 01:00:50 +00:00
dependabot[bot] 66c271a530 build(deps): bump the backend group across 1 directory with 17 updates
Bumps the backend group with 17 updates in the /backend directory:

| Package | From | To |
| --- | --- | --- |
| [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) | `2.0.52` | `2.0.55` |
| [flask-migrate](https://github.com/miguelgrinberg/flask-migrate) | `4.0.7` | `4.1.0` |
| [flask-jwt-extended](https://github.com/vimalloc/flask-jwt-extended) | `4.6.0` | `4.7.4` |
| [flask-cors](https://github.com/corydolphin/flask-cors) | `6.0.2` | `6.0.5` |
| [flask-socketio](https://code.miguelgrinberg.com/miguelgrinberg/flask-socketio) | `5.6.1` | `5.7.0` |
| [python-socketio](https://github.com/sponsors/miguelgrinberg) | `5.16.3` | `5.17.1` |
| [python-engineio](https://code.miguelgrinberg.com/miguelgrinberg/python-engineio) | `4.13.3` | `4.15.1` |
| [python-dotenv](https://github.com/theskumar/python-dotenv) | `1.2.2` | `1.2.4` |
| [pyyaml](https://github.com/yaml/pyyaml) | `6.0.1` | `6.0.3` |
| [click](https://github.com/pallets/click) | `8.3.3` | `8.5.0` |
| [croniter](https://github.com/pallets-eco/croniter) | `6.0.0` | `6.2.4` |
| [cryptography](https://github.com/pyca/cryptography) | `50.0.0` | `50.0.2` |
| [pyotp](https://github.com/pyauth/pyotp) | `2.9.0` | `2.10.0` |
| [requests](https://github.com/psf/requests) | `2.33.1` | `2.34.2` |
| [boto3](https://github.com/boto/boto3) | `1.35.0` | `1.43.123` |
| [apispec](https://github.com/marshmallow-code/apispec) | `6.4.0` | `6.10.0` |
| [prompture](https://github.com/jhd3197/prompture) | `1.11.0` | `1.16.0` |



Updates `sqlalchemy` from 2.0.52 to 2.0.55
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst)
- [Commits](https://github.com/sqlalchemy/sqlalchemy/commits)

Updates `flask-migrate` from 4.0.7 to 4.1.0
- [Release notes](https://github.com/miguelgrinberg/flask-migrate/releases)
- [Changelog](https://github.com/miguelgrinberg/Flask-Migrate/blob/main/CHANGES.md)
- [Commits](https://github.com/miguelgrinberg/flask-migrate/compare/v4.0.7...v4.1.0)

Updates `flask-jwt-extended` from 4.6.0 to 4.7.4
- [Release notes](https://github.com/vimalloc/flask-jwt-extended/releases)
- [Commits](https://github.com/vimalloc/flask-jwt-extended/compare/4.6.0...4.7.4)

Updates `flask-cors` from 6.0.2 to 6.0.5
- [Release notes](https://github.com/corydolphin/flask-cors/releases)
- [Changelog](https://github.com/corydolphin/flask-cors/blob/main/CHANGELOG.md)
- [Commits](https://github.com/corydolphin/flask-cors/compare/6.0.2...6.0.5)

Updates `flask-socketio` from 5.6.1 to 5.7.0

Updates `python-socketio` from 5.16.3 to 5.17.1
- [Commits](https://github.com/sponsors/miguelgrinberg/commits)

Updates `python-engineio` from 4.13.3 to 4.15.1

Updates `python-dotenv` from 1.2.2 to 1.2.4
- [Release notes](https://github.com/theskumar/python-dotenv/releases)
- [Changelog](https://github.com/theskumar/python-dotenv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/theskumar/python-dotenv/compare/v1.2.2...v1.2.4)

Updates `pyyaml` from 6.0.1 to 6.0.3
- [Release notes](https://github.com/yaml/pyyaml/releases)
- [Changelog](https://github.com/yaml/pyyaml/blob/6.0.3/CHANGES)
- [Commits](https://github.com/yaml/pyyaml/compare/6.0.1...6.0.3)

Updates `click` from 8.3.3 to 8.5.0
- [Release notes](https://github.com/pallets/click/releases)
- [Changelog](https://github.com/pallets/click/blob/main/CHANGES.md)
- [Commits](https://github.com/pallets/click/compare/8.3.3...8.5.0)

Updates `croniter` from 6.0.0 to 6.2.4
- [Release notes](https://github.com/pallets-eco/croniter/releases)
- [Changelog](https://github.com/pallets-eco/croniter/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pallets-eco/croniter/compare/6.0.0...6.2.4)

Updates `cryptography` from 50.0.0 to 50.0.2
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/50.0.0...50.0.2)

Updates `pyotp` from 2.9.0 to 2.10.0
- [Release notes](https://github.com/pyauth/pyotp/releases)
- [Changelog](https://github.com/pyauth/pyotp/blob/main/Changes.rst)
- [Commits](https://github.com/pyauth/pyotp/compare/v2.9.0...v2.10.0)

Updates `requests` from 2.33.1 to 2.34.2
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.33.1...v2.34.2)

Updates `boto3` from 1.35.0 to 1.43.123
- [Release notes](https://github.com/boto/boto3/releases)
- [Commits](https://github.com/boto/boto3/compare/1.35.0...1.43.123)

Updates `apispec` from 6.4.0 to 6.10.0
- [Changelog](https://github.com/marshmallow-code/apispec/blob/dev/CHANGELOG.rst)
- [Commits](https://github.com/marshmallow-code/apispec/compare/6.4.0...6.10.0)

Updates `prompture` from 1.11.0 to 1.16.0
- [Release notes](https://github.com/jhd3197/prompture/releases)
- [Commits](https://github.com/jhd3197/prompture/compare/v1.11.0...v1.16.0)

---
updated-dependencies:
- dependency-name: apispec
  dependency-version: 6.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: backend
- dependency-name: boto3
  dependency-version: 1.43.123
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: backend
- dependency-name: click
  dependency-version: 8.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: backend
- dependency-name: croniter
  dependency-version: 6.2.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: backend
- dependency-name: cryptography
  dependency-version: 50.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: backend
- dependency-name: flask-cors
  dependency-version: 6.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: backend
- dependency-name: flask-jwt-extended
  dependency-version: 4.7.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: backend
- dependency-name: flask-migrate
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: backend
- dependency-name: flask-socketio
  dependency-version: 5.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: backend
- dependency-name: prompture
  dependency-version: 1.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: backend
- dependency-name: pyotp
  dependency-version: 2.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: backend
- dependency-name: python-dotenv
  dependency-version: 1.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: backend
- dependency-name: python-engineio
  dependency-version: 4.15.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: backend
- dependency-name: python-socketio
  dependency-version: 5.17.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: backend
- dependency-name: pyyaml
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: backend
- dependency-name: requests
  dependency-version: 2.34.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: backend
- dependency-name: sqlalchemy
  dependency-version: 2.0.55
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: backend
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-03 00:52:04 +00:00
dependabot[bot] 67a2e972db build(deps): bump websockets from 15.0.1 to 16.1.1 in /backend
Bumps [websockets](https://github.com/python-websockets/websockets) from 15.0.1 to 16.1.1.
- [Release notes](https://github.com/python-websockets/websockets/releases)
- [Commits](https://github.com/python-websockets/websockets/compare/15.0.1...16.1.1)

---
updated-dependencies:
- dependency-name: websockets
  dependency-version: 16.1.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-03 00:42:50 +00:00
dependabot[bot] 07c0d7d4e3 build(deps): bump gunicorn from 23.0.0 to 26.2.0 in /backend
Bumps [gunicorn](https://github.com/benoitc/gunicorn) from 23.0.0 to 26.2.0.
- [Release notes](https://github.com/benoitc/gunicorn/releases)
- [Commits](https://github.com/benoitc/gunicorn/compare/23.0.0...26.2.0)

---
updated-dependencies:
- dependency-name: gunicorn
  dependency-version: 26.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-03 00:33:41 +00:00
dependabot[bot] 5b0b0550fc build(deps): bump marshmallow from 3.26.2 to 4.3.1 in /backend
Bumps [marshmallow](https://github.com/marshmallow-code/marshmallow) from 3.26.2 to 4.3.1.
- [Changelog](https://github.com/marshmallow-code/marshmallow/blob/dev/CHANGELOG.rst)
- [Commits](https://github.com/marshmallow-code/marshmallow/compare/3.26.2...4.3.1)

---
updated-dependencies:
- dependency-name: marshmallow
  dependency-version: 4.3.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-02 23:57:34 +00:00
Juan Denis d723d887e7 Bump Authlib to 1.9.1 2026-11-02 18:49:06 -05:00
Juan Denis 2f41a9222f Re-sync bundled registry index with the published one 2026-11-02 18:36:38 -05:00
Juan Denis 2521da0e46 Add an SSH Access server permission profile
Faro access grants install keys through the agent's ssh:keys
actions. A locked-down server had no canned profile for that short
of Full Access; ssh_access grants ssh:keys:* plus the health reads.
2026-10-26 11:16:21 -04:00
Juan Denis cdd6d0898d Drop the unused schedule and passlib packages
Each survived as a single import nothing called: scheduling moved to
the job runner, and the FTP service never used sha512_crypt.
2026-10-26 01:44:23 -04:00
Juan Denis a9668000da Keep the new route logic behind services
The SSL-enable and server-overview changes put session and query
calls in the routes, tripping the controller-boundary ratchet. Move
them into SiteDomainService and WorkspaceService, raise a typed error
for bad allowed_ips, point the ACME-contact test at the new vhost
door, and regenerate the baseline and inventory (476 -> 453).
2026-10-26 01:43:12 -04:00
Juan Denis e3131659c2 Satisfy the test seed ratchet
The custom-domain SSL test seeded its app by hand instead of through
make_application, and test_manifest_reach.py no longer seeds one, so
its baseline entry went stale.
2026-10-26 01:03:40 -04:00
Juan Denis 411ce694da Remove the orphaned manifest preview service
The generic preview provisioner replaced its only caller; only its
own tests still reached it, proving a path production no longer
takes.
2026-10-26 01:02:35 -04:00
Juan Denis 1fe8ada10d Look up agent registration tokens by digest
POST /servers/register needs no login and ran a slow PBKDF2 check
against every pending server per request, so a few IPs under the
5/min limit could keep the single worker's CPU busy. Tokens carry 256
random bits, so store a SHA-256 digest and find the row directly;
hashes issued before this still verify until they expire.
2026-10-26 01:00:48 -04:00
Juan Denis a915e95474 Send container log streams only to their viewer
Every subscriber starts its own docker logs process but emitted into
the shared app room, so with two viewers open each saw every line
twice, got the other's tail replayed, and both saw 'ended' when either
stream stopped. Deliver to the owning socket while room membership
still gates it, keeping the per-line access re-check.
2026-10-26 00:59:21 -04:00
Juan Denis c5d0597140 Make login-link redemption truly single-use
redeem() checked used_at, then set it and committed, so two requests
arriving together (a mail-preview bot and the user, or an attacker
racing them) both passed the check and both got a session. Claim the
link with one conditional UPDATE and refuse when no row changed.
2026-10-26 00:57:28 -04:00
Juan Denis 2dee9fa891 Pass DB passwords via env, not the command line
docker exec -e MYSQL_PWD=<pw>, mysqldump -p<pw> and friends put the
root DB password in argv, readable by every local account through ps
or /proc. Add secret_env() and hand the password to the child's
environment (bare -e NAME for docker exec), and drop it from the
mysqladmin readiness ping, which never needed it.
2026-10-26 00:56:42 -04:00
Juan Denis 83ea2c59d4 Write cron and storage settings atomically
Both files used a truncating open() and their loaders read damage as
empty, so a full disk mid-write reset off-site backups to local and
the next cron add replaced every job's metadata with the new job.
Route both through the config store's temp+replace write; an
unreadable file is moved aside instead of being overwritten.
2026-10-26 00:53:25 -04:00
Juan Denis a79d5010c2 Escape % in crontab commands
cron treats a bare % as end-of-command and pipes the rest to stdin,
so a job like 'date +%F >> log' ran as 'date +' on schedule while
Run now (bash -c) ran it correctly. Write lines with \%, unescape
when parsing, and locate a job's line by parsing in edit and
tracking so legacy unescaped lines are still found and fixed.
2026-10-26 00:51:38 -04:00
Juan Denis cb93120bac Keep custom-domain HTTPS across vhost rebuilds
Enabling SSL on a custom domain edited the vhost in place, but every
later rewrite (deploy, slot switch, domain attach, drift repair,
restore) rebuilt it from Domain rows that only knew wildcard certs,
so HTTPS silently disappeared while the row still said enabled.

Rebuilds now resolve each domain's own cert and render one server set
per certificate when domains differ. Enable/attach publish through
write_app_vhost and only claim SSL when nginx accepted it, the key
path is stored under its real column (legacy rows fall back to
certbot's privkey.pem), disable drops the HTTPS block, and a repeat
add_ssl_to_site swaps the cert instead of breaking nginx -t.
2026-10-26 00:49:57 -04:00
Juan Denis f724cf0067 Keep live sockets alive past access-token expiry
Every emit re-validated the socket's saved JWT including exp, and
access tokens last 15 minutes, so metrics, terminals, logs and
notifications were cut a quarter-hour after connecting; clients do
not reconnect after a server-side disconnect. Re-checks now skip exp
only; revocation, auth_version and deactivation still end the socket,
and connecting still needs an unexpired token.
2026-10-26 00:44:28 -04:00
Juan Denis 475add2674 Validate Postgres helper SQL and report user errors
pg_create_database/user and pg_grant/revoke built superuser SQL from
raw names and passwords, unlike the MySQL helpers beside them. A quote
in a password broke CREATE USER, and the create routes ignored that
and returned credentials for a user that did not exist. Validate
identifiers and privilege lists, escape the password, and surface a
failed user as user_error instead of fake credentials.
2026-10-26 00:43:13 -04:00
Juan Denis 84d6cdef4c Block file ops on parents of protected roots
PROTECTED_ROOTS only matched the path the user typed, so copying /opt
copied /opt/serverkit/.env (JWT key, DB creds) somewhere readable,
and deleting or moving /opt took the running panel with it. Refuse
copy, move, rename and delete when a protected root lies inside the
source, and keep symlinks as links when copying a tree.
2026-10-26 00:39:26 -04:00
Juan Denis e1835149a7 Require a verified email before SSO account linking
find_or_create_user attached a new SSO identity to any user with the
same email, but Google and generic OIDC profiles never carried
email_verified. An IdP account showing an unverified copy of the
admin's address signed in as the admin. Carry the claim through and
refuse to link without it; GitHub and SAML already vouch.
2026-10-26 00:37:57 -04:00
Juan Denis 8ab5ab1bfc Stop SSO from auto-creating developer accounts
Auto-provision defaulted on with role developer and no domain filter,
so enabling GitHub login for yourself let any GitHub user sign up and
open remote terminals, while password registration stayed closed.
Default to off and viewer; turning it on stays an explicit choice.
2026-10-26 00:36:57 -04:00
Juan Denis 9b85952b5f Enforce workspace access on every server route
Only three routes checked can_access_server; the rest loaded the server
by id, so a developer outside a workspace could run cron, write files,
install packages or widen agent permissions on its servers, and the
list/overview/docker picker showed every server. Every <server_id>
route now resolves through the workspace check, lists filter in SQL,
and PUT /servers/<id> validates allowed_ips like PUT /allowed-ips.
A sweep test walks the URL map so a new unguarded route fails CI.
2026-10-26 00:35:55 -04:00
Juan Denis b1260f7ea5 Catch plan citations at commit time
The guard only ran inside a backend pytest shard, so a citation in
SCSS or JSX surfaced minutes after the push. The pre-commit hook now
runs the same test file in --staged mode on every staged app file,
reusing its patterns and PENDING allowlist so the two cannot drift.
2026-10-25 23:27:21 -04:00
Juan Denis e30fe7c081 fix: refresh preview API and controller boundary inventories 2026-10-24 14:50:57 -04:00
Juan Denis caab657806 Merge remote-tracking branch 'origin/dev' into dev 2026-10-24 14:44:37 -04:00
Juan Denis e955134e41 feat: run approved assistant deployments through persistent recipe operations 2026-10-24 06:30:26 -04:00
Juan Denis a3d64e5a52 fix: preserve container port semantics for previews of slot deployments 2026-10-24 06:24:18 -04:00
Juan Denis c0dbb921e1 feat: queue preview automation and preserve cleanup ownership through app deletion 2026-10-24 06:21:26 -04:00
Juan Denis 5a13a0dfb1 feat: provision isolated generic previews with retryable resource cleanup 2026-10-24 06:11:46 -04:00
Juan Denis 355157856f feat: add effective environment inspector with browser regression checks 2026-10-24 06:02:51 -04:00
Juan Denis d4f6b2af55 feat: expose value-free effective environment provenance through app access boundary 2026-10-24 05:58:02 -04:00
Juan Denis 0894b8ff5b fix: require provisioned runtime before advertising previews 2026-10-24 05:56:04 -04:00
Juan Denis 9c934cb7a6 test: enforce pure nginx configuration boundaries 2026-10-24 05:36:59 -04:00
Juan Denis 69a9ad345a refactor: separate nginx configuration rendering from host operations 2026-10-24 05:36:03 -04:00
Juan Denis 2a030b31ef test: capture nginx rendering contract before extraction 2026-10-24 05:33:01 -04:00
Juan Denis ac0871c016 fix: start agent maintenance only in the guarded runtime phase 2026-10-24 05:14:36 -04:00
Juan Denis 3c5b948f2e test: enforce core boundaries and document extension rules 2026-10-24 04:48:54 -04:00
Juan Denis 4898fccae1 refactor: separate backend startup phases and guard ordering 2026-10-24 04:30:14 -04:00
Max Freedom PollardandClaude Opus 5.5 3de85c2a17 fix(security): honour the failed-login time window (#180)
check_failed_logins computed a cutoff from since_hours and never used
it, so the Security -> Events "Failed login attempts (24h)" card and
its alert counted every failure still in /var/log/auth.log or
/var/log/secure: up to a week of SSH attempts under the usual weekly
log rotation.

Each matching line is now dated from its stamp, either RFC 3339
(rsyslog's high-precision format) or the traditional BSD syslog stamp
(no year, so a December line read in January is placed in the previous
year), and lines older than the cutoff are skipped. A line whose stamp
is not recognised is still counted rather than hidden. The log paths
move to SecurityService.AUTH_LOGS so the tests can point at a temp
file.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 04:27:36 -04:00
Juan Denis 771fca6ef0 fix(registry): sync bundled WordPress 1.0.4 release metadata 2026-10-23 18:50:18 -04:00
Juan Denis 1a6335e7cf fix: remember agent IP alerts across panel restarts 2026-10-23 18:05:15 -04:00
Juan Denis b85cc18220 fix: require patched PHP runtimes in WordPress templates 2026-10-23 18:05:08 -04:00
Juan Denis 90c8fb0eac Close the bundled Gitea to the internet and signups
Same fix as the registry's template: Gitea 1.21 was reachable on
every interface with open registration, and a panel's Gitea was
exploited to mine Monero. Bind the web port to 127.0.0.1 behind the
panel's /gitea proxy, disable signups and move to Gitea 1.27.
2026-10-11 17:57:55 -04:00
Juan Denis fe42d19048 Feature templates the registry marks as featured
The Templates page only featured a hard-coded list, so `featured: true`
in a template (Prompture Hub, Torre) never reached the Featured-first
view. The page now honors the flag alongside the list, and bundled
templates carry it into the catalog and the published index.
2026-10-11 16:41:38 -04:00
Juan Denis b86c03dc6b Don't 500 on uptime stop when its data dir is unwritable
Stop now persists the tracking flag, and the save created the data
directory unguarded. Where that fails (an unprivileged host or CI
runner) the save is now logged and reported false, as its docstring
already promised, instead of raising.
2026-10-11 00:52:44 -04:00
Juan Denis 498901e70e Keep the panel booting when uptime can't resume
Resuming uptime tracking at boot read the history through a helper
that first created /var/lib/serverkit. The image runs unprivileged, so
that makedirs raised and the app never started. Reading now creates
nothing, and a failed resume is logged instead of aborting boot.
2026-10-11 00:50:14 -04:00
Juan Denis 6cfebf8a54 Let the load-average test run where os lacks it
Windows has no os.getloadavg, so patching it raised before the test
could run; create=True supplies the attribute.
2026-10-10 23:44:20 -04:00