Technical article
Deployment security: package, deploy, and roll back through one guarded API
In production, a LightESB service reaches the runtime through the deployment management API or the CLI — both are remote writes against a running control plane. The server treats every uploaded archive as untrusted input: the directory contract and extraction boundary are checked before any file is copied or any route is started.
Deployment is a remote write against a running server
The CLI carries no Camel runtime and never bypasses the server-side state machine. Every write operation requires an explicit `--yes`, verifies real-path boundaries, and replaces target files atomically — which makes the package upload the critical trust boundary of the whole flow.
Both the deploy and the validate-only endpoints check the service directory structure first: every `serviceName + serviceVersion` directory must contain `service.config.properties` and exactly one `*.xml` route file. A missing XML or multiple XMLs fail validation — no service files are copied and no routes are started.
Four lines of defense
Hardened archive intake
Uploads accept .zip, .tar.gz, and .tgz under controlled temporary filenames. Path traversal, duplicate files, TAR link or special entries, and archives over the entry-count, single-file, total-extraction, or directory-depth limits are rejected with 400 DEPLOYMENT_VALIDATION_ERROR; upload/batch shares the same boundary.
Validate without deploying
POST /api/deployment/validate and lightesb deploy validate run the same directory-contract check without copying files or starting routes — a dry-run gate for CI and pre-delivery review.
Server-owned target
The deployment target is decided by lightesb.route.directory on the server. The legacy targetDirectory parameter returns 400 TARGET_DIRECTORY_UNSUPPORTED, and the CLI offers no --target-directory — clients never choose where service files land.
Explicit confirmation
lightesb deploy upload requires --yes; a missing confirmation exits with code 64. HTTP error summaries keep the status, error code, and actionable detail while redacting token, password, secret, authorization, and apiKey credentials.
Real commands: upload, history, rollback
The API flow — upload with autoStart, page the filtered history, then roll back by deploymentId:
curl -X POST "http://localhost:8080/api/deployment/upload" \
-F "file=@PlatformHttp-v3.0.0.zip" \
-F "autoStart=true"
curl "http://localhost:8080/api/deployment/history?limit=50&serviceName=PlatformHttp&serviceVersion=v3.0.0"
curl -X POST "http://localhost:8080/api/deployment/rollback/deploy-550e8400-e29b-41d4-a716-446655440000?autoStart=true"The CLI mirrors the same endpoints, from packaging to history:
lightesb service package build --file package.json --yes
lightesb service package deploy --file package.json --yes
lightesb deploy validate ./DemoSrv.zip
lightesb deploy upload ./DemoSrv.zip --yes
lightesb deploy upload ./DemoSrv.zip --no-auto-start --yes
lightesb deploy status <deploymentId>
lightesb deploy history --service-name DemoSrv --service-version 1.0.0 --limit 20Rollback semantics and port checks
Rollback restores the backup state captured before the specified deployment — not a snapshot of the files after it succeeded. First deployments and records without a backup directory cannot roll back and return 400 with errorCode=ROLLBACK_NOT_AVAILABLE; the same error appears when the backup is gone or sits outside the allowed backup root.
With autoStart=true the server restores the files, then actively loads the restored XML and verifies route state instead of relying on file-watch events — a restored configuration with server.running=false completes as a stopped-state success. Before loading, port occupation is checked from the restored HTTP.Listener/server.port/port.level: a conflict in per-version mode (port.level=version) fails with the occupying service, while shared-port mode on both sides may reuse the port.
Boundaries and where to troubleshoot
The history list returns summaries only — deploymentId, operationType (DEPLOY or ROLLBACK), status such as SUCCESS, FAILED, ROLLED_BACK, or ROLLBACK_FAILED, and backupAvailable — never full step logs; GET /history/detail/{deploymentId} carries the deployLogs for the validation, backup, file-overwrite, and route-loading steps. deploy history defaults to the latest 50 records and maps to GET /api/deployment/history. Treat upload and rollback as privileged remote writes: run them only with explicit authorization, and script them against the documented exit codes — 0 success, 64 missing confirmation, 69 server-side failure.