Repository navigation
feat(server): deploy the API to Serverpod Cloud with health version and CORS gate - #115
Conversation
Deploying puls3 with
|
| Latest commit: |
2bf134c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://f16545f0.puls3-4lw.pages.dev |
| Branch Preview URL: | https://chore-31-serverpod-cloud-dep.puls3-4lw.pages.dev |
fa08b9b to
011090b
Compare
|
Added in 011090b: when the Pages project sets |
011090b to
c2c1cb4
Compare
c2c1cb4 to
d6f09b5
Compare
health.check returns the package version, plus +<sha> when PULS3_GIT_SHA is set, so a deployed server names the commit it runs. A unit test keeps the version constant equal to pubspec.yaml, and an invalid value stops the server at startup.
Serverpod 3.4.13 sends a static wildcard Access-Control-Allow-Origin and answers preflights in its core middleware, before added middleware runs. An origin gate on the API server now rejects requests whose Origin is not loopback or listed in PULS3_ALLOWED_ORIGINS with 403 before any endpoint runs, and echoes allowed origins with Vary: Origin. Requests without an Origin header (curl, probes) pass through.
scloud.yaml uses the format serverpod_cloud_cli 1.0.0 writes, with a placeholder project id that scloud project link replaces, and no pre-deploy scripts: the Flutter web app ships to Cloudflare Pages and the generated code is committed. The root .scloudignore keeps passwords, run-mode configs, tests, Docker files and web/app out of the upload; paths are workspace-relative because scloud reads it from the workspace root.
Document the first deploy (scloud install, login, project create and link, variables, platform-managed passwords), the mapping of each secret in docs/infra/secrets.md (#30) to Serverpod Cloud, the redeploy flow with the health check, the PULS3_GIT_SHA version and the PULS3_ALLOWED_ORIGINS allow-list. The tracker stays off in this deployment.
Loopback origins (localhost, 127.0.0.1, [::1]) are now allowed only in the development run mode; production, staging and test allow just the origins listed in PULS3_ALLOWED_ORIGINS. A request with several Origin headers is rejected. server.dart builds the gate through originGateFromEnvironment, which is tested end to end over HTTP, and new cases cover default-port and duplicate normalization, Vary merging, an existing Access-Control-Allow-Origin and lookalike loopback hosts.
scloud 1.0.0 runs scripts.pre_deploy on the deploying machine, in the server directory, through cmd /c on Windows and bash -c elsewhere, before it zips the project, and stops the deploy on a non-zero exit. The new tool/set_deploy_sha.dart sets PULS3_GIT_SHA to git rev-parse --short HEAD (suffixed -dirty when tracked files changed) with scloud variable set. scloud.yaml now runs it and explains the project id placeholder.
First deploy is now seven ordered steps without jumps: a pinned CLI install (1.0.1 on Dart 3.12.2+, or 1.0.0 with serverpod_cloud_shared pinned on older Dart), login, project create with an explicit plan, link, variables, deploy with the PULS3_GIT_SHA pre-deploy script, and verification including CORS checks (200 for the Pages origin, 403 for another). Adds a failure section (build log, logs, deployment status, fix forward or redeploy a good commit) that marks the Cloud behaviour the CLI cannot confirm, and documents that loopback origins are allowed in development only.
Loopback origins are allowed only in the development run mode, and Serverpod Cloud's run mode is not verified yet. A localhost-origin call that returns 200 after deploy reveals a non-production run mode.
…ealth checks The Cloud load balancer answers 411 to a POST without Content-Length, so the verify commands now send an empty JSON body. Refs #31
…3_API_URL When the Pages project sets PULS3_API_URL, the build passes it as a dart-define, so the web app calls the Serverpod Cloud server. Refs #31
d6f09b5 to
2bf134c
Compare
moises-cisneros
left a comment
There was a problem hiding this comment.
Tested locally on Flutter 3.44.4 (Dart 3.12.2, same as CI): dart analyze --fatal-infos is clean, 325 unit tests pass, and scripts/tests/cloudflare-pages-build.test.sh passes 7/7. I also probed the live API: /health/check returns 1.0.0+b88cdaf, the Pages origin gets 200, evil.example.com and localhost:3000 get 403.
Before merging
- #114 is already merged, but this PR still targets
chore/110-serverpod-4-upgrade. Please retarget it tomainso the full CI runs.
Minor observations (non-blocking)
set_deploy_sha.dartuses--untracked-files=no, so an untracked file that is not in.scloudignoreis uploaded while the health version still reports a clean SHA.PULS3_GIT_SHAis set before the upload. If the upload or build fails, the variable already holds the new SHA while the old build keeps running.- An
OPTIONSpreflight from a disallowed origin getsaccess-control-allow-origin: *from Serverpod core. The real request is still rejected with 403, so it is not exploitable, but the header is misleading. The doc comment onoriginGatealready explains why.
The open acceptance criteria (app URL, Pages deploy, redeploy by a teammate) are acknowledged in the description.
#117) * build(serverpod)!: upgrade runtime and generated APIs to 4.0.4 * fix(migrations): preserve auth rate-limit rows in Serverpod 4 upgrade Copy serverpod_auth_idp_rate_limited_request_attempt rows through a transaction-scoped temp table while the generator recreates the table to rename nonce to key. Document the upgrade, migration and rollback steps. Refs #110 * build(server): bundle Serverpod 4 server and keep local secrets out of the image Serverpod 4 dependencies use Dart build hooks, so the image now uses dart build cli and ships the emitted bundle. The new .dockerignore keeps .env files and config/passwords.yaml out of the build context, which previously baked local secrets into the runtime image. Refs #110 * ci: pin Flutter 3.44.4 / Serverpod 4.0.4 and build the server image Add a docker job, triggered by server, domain, lockfile and .dockerignore changes, that builds the image with planted secret files and asserts the bundle exists and passwords.yaml is excluded. Refs #110 * docs: update toolchain pins to Flutter 3.44.4 and Serverpod 4.0.4 Refs #110 * build(flutter): pin the Cloudflare Pages Flutter version in the repository The Pages build command cloned Flutter 3.41.4 from the dashboard, so every branch on Serverpod 4 (Dart ^3.12.2) failed version solving. The build now runs scripts/cloudflare-pages-build.sh, which installs the branch's pinned Flutter. A test keeps its version in sync with CI. Refs #110 * docs(env): list PULS3_FLUTTER_DIR in .env.example Refs #110 * fix(migrations): recreate the Serverpod 4 upgrade migration on top of the hire tables The forced upgrade-4-0 migration was created before #93 added migration 20261006210458593 with the hire and hire_payment tables, so its snapshot did not know about them. Recreate it with serverpod_cli 4.0.4 as 20261007214534790-upgrade-4-0 so it now sorts after 20261006210458593 and its definition includes hire, hire_payment, chain_submission and the Serverpod 4 module tables. The migration does not create, drop or alter the hire tables. The hand edit is re-applied: rows of serverpod_auth_idp_rate_limited_request_attempt are copied to a temporary table before Serverpod drops and recreates it, with nonce mapped to key, and restored afterwards, so rate-limit history still survives the upgrade. Refs #110 * docs(env): list PULS3_HIRE_JOB_DURATION_SECONDS in .env.example #93 reads it in HireService without listing it, which fails scripts/tests/env-inventory.test.sh on main. Refs #110 * ci: run the offline script tests on every change scripts/tests/*.test.sh were never run in CI, so main broke the env inventory (PULS3_HIRE_JOB_DURATION_SECONDS) without a red check. The new scripts job runs them all and gates the merge. Refs #110 * feat(server): deploy the API to Serverpod Cloud with health version and CORS gate (#115) * feat(server): report the deployed commit in the health version health.check returns the package version, plus +<sha> when PULS3_GIT_SHA is set, so a deployed server names the commit it runs. A unit test keeps the version constant equal to pubspec.yaml, and an invalid value stops the server at startup. * feat(server): restrict browser callers to allowed origins Serverpod 3.4.13 sends a static wildcard Access-Control-Allow-Origin and answers preflights in its core middleware, before added middleware runs. An origin gate on the API server now rejects requests whose Origin is not loopback or listed in PULS3_ALLOWED_ORIGINS with 403 before any endpoint runs, and echoes allowed origins with Vary: Origin. Requests without an Origin header (curl, probes) pass through. * chore(server): add Serverpod Cloud project config scloud.yaml uses the format serverpod_cloud_cli 1.0.0 writes, with a placeholder project id that scloud project link replaces, and no pre-deploy scripts: the Flutter web app ships to Cloudflare Pages and the generated code is committed. The root .scloudignore keeps passwords, run-mode configs, tests, Docker files and web/app out of the upload; paths are workspace-relative because scloud reads it from the workspace root. * docs: add Serverpod Cloud deploy and redeploy steps Document the first deploy (scloud install, login, project create and link, variables, platform-managed passwords), the mapping of each secret in docs/infra/secrets.md (#30) to Serverpod Cloud, the redeploy flow with the health check, the PULS3_GIT_SHA version and the PULS3_ALLOWED_ORIGINS allow-list. The tracker stays off in this deployment. * fix(server): allow loopback origins only in development Loopback origins (localhost, 127.0.0.1, [::1]) are now allowed only in the development run mode; production, staging and test allow just the origins listed in PULS3_ALLOWED_ORIGINS. A request with several Origin headers is rejected. server.dart builds the gate through originGateFromEnvironment, which is tested end to end over HTTP, and new cases cover default-port and duplicate normalization, Vary merging, an existing Access-Control-Allow-Origin and lookalike loopback hosts. * feat(server): set PULS3_GIT_SHA in a Serverpod Cloud pre-deploy script scloud 1.0.0 runs scripts.pre_deploy on the deploying machine, in the server directory, through cmd /c on Windows and bash -c elsewhere, before it zips the project, and stops the deploy on a non-zero exit. The new tool/set_deploy_sha.dart sets PULS3_GIT_SHA to git rev-parse --short HEAD (suffixed -dirty when tracked files changed) with scloud variable set. scloud.yaml now runs it and explains the project id placeholder. * docs(server): make the Serverpod Cloud first deploy one linear path First deploy is now seven ordered steps without jumps: a pinned CLI install (1.0.1 on Dart 3.12.2+, or 1.0.0 with serverpod_cloud_shared pinned on older Dart), login, project create with an explicit plan, link, variables, deploy with the PULS3_GIT_SHA pre-deploy script, and verification including CORS checks (200 for the Pages origin, 403 for another). Adds a failure section (build log, logs, deployment status, fix forward or redeploy a good commit) that marks the Cloud behaviour the CLI cannot confirm, and documents that loopback origins are allowed in development only. * chore(server): recommend --from-file for Serverpod Cloud passwords * docs(server): check that the deployed API rejects localhost origins Loopback origins are allowed only in the development run mode, and Serverpod Cloud's run mode is not verified yet. A localhost-origin call that returns 200 after deploy reveals a non-production run mode. * chore(server): link Serverpod Cloud project puls3-hub-on-stellar Refs #31 * docs(server): record the Serverpod Cloud API URL and send a body in health checks The Cloud load balancer answers 411 to a POST without Content-Length, so the verify commands now send an empty JSON body. Refs #31 * feat(flutter): point the Pages build at the deployed API through PULS3_API_URL When the Pages project sets PULS3_API_URL, the build passes it as a dart-define, so the web app calls the Serverpod Cloud server. Refs #31 * docs(env): list PULS3_ALLOWED_ORIGINS and PULS3_GIT_SHA in .env.example Refs #31
Refs #31
Summary
Server side of #31: the API is live on Serverpod Cloud at
https://puls3-hub-on-stellar.api.serverpod.space/./health/checkreturnsversionfrom the pubspec plusPULS3_GIT_SHA. A pre-deploy script (tool/set_deploy_sha.dart, run byscloud deploythroughscloud.yaml) sets that variable to the deployed short SHA, with-dirtyadded when tracked files have uncommitted changes.PULS3_ALLOWED_ORIGINS. Loopback origins are allowed only in thedevelopmentrun mode, so the deployed server rejectslocalhost.scloud.yamlis linked topuls3-hub-on-stellar, and.scloudignorekeeps.env,passwords.yamland tests out of the upload.Acceptance criteria
healthendpoint on the public server returns the deployed version (1.0.0+b88cdaf, below).puls3_server/README.md.scloud deploy --wet-run --show-fileslists.envandconfig/passwords.yamlas ignored, and the Cloud-managed passwords are generated by the platform.puls3_server/README.md; the app URL comes with the Pages deploy.Deliverable 6 (the indexer/tracker running on the deployed server) is not enabled:
PULS3_TRACKER_ENABLEDis left unset until #96.Verification evidence
Notes for reviewers
POSTwithoutContent-Length, so every verifycurlin the README sends-d '{}'.SERVERPOD_APPLY_MIGRATIONS=trueandPULS3_ALLOWED_ORIGINS=https://puls3-4lw.pages.dev.PULS3_GIT_SHAis set by the pre-deploy script.PULS3_STELLAR_*use the testnet defaults.PULS3_ALLOWED_ORIGINSandPULS3_GIT_SHAshould be added to.env.exampleanddocs/infra/secrets.md(follow-up).scloudmust be run asscloud.bator throughcmd //c "scloud ..."; the README notes this.