Skip to content

feat: orchestrator build backend with Deployments module#12479

Open
deepshekhardas wants to merge 1 commit into
appwrite:1.9.xfrom
deepshekhardas:feat/orchestrator-build-backend
Open

feat: orchestrator build backend with Deployments module#12479
deepshekhardas wants to merge 1 commit into
appwrite:1.9.xfrom
deepshekhardas:feat/orchestrator-build-backend

Conversation

@deepshekhardas
Copy link
Copy Markdown

Changes

  • Add Deployments module with Build/Action, Build/Update, Source/Get, Events/Create endpoints
  • Update Builds worker for orchestrator-driven deployment pipelines
  • Add build artifacts action for deployment tracking
  • Register new HTTP routes in Deployments/Services/Http.php

Closes #12441

…#12441)

- Add Deployments module with Build/Action, Build/Update, Source/Get, Events/Create endpoints
- Update Builds worker for orchestrator-driven deployment pipelines
- Add build artifacts action for deployment tracking
- Register new HTTP routes in Deployments/Services/Http.php
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 3, 2026

Greptile Summary

This PR introduces an orchestrator-driven build backend for Appwrite deployments, adding a new Deployments module with endpoints for uploading build artifacts, streaming source artifacts, and receiving HMAC-signed callback events from the orchestrator. The Builds worker is extended with ~834 lines of new logic to handle orchestrator lifecycle events (log streaming, artifact readiness, exit handling) in addition to the existing executor path.

  • New module registers three HTTP endpoints (GET /v1/deployments/:id/artifacts/source, PUT /v1/deployments/:id/artifacts/build, POST /v1/deployments/:id/events) gated by a new TOKENS_RESOURCE_TYPE_DEPLOYMENT_ARTIFACTS token type.
  • Builds worker gains createOrchestratorBuild, applyOrchestratorEvent, applyOrchestratorExit, and completeOrchestratorDeployment methods that mirror and extend the existing executor build flow.
  • Cancellation (Status/Update.php) now appends a log entry, cleans up artifact tokens, and routes runtime cleanup to either the orchestrator or executor based on _APP_BUILDS_BACKEND.

Confidence Score: 2/5

Not safe to merge without fixes: premature event emission on partial chunk uploads and missing authorization skips in the new worker methods are active defects on the changed paths.

Three active defects were found on changed code paths: (1) both Functions and Sites deployment creation now fire events after every chunk upload instead of only the final one, which can trigger build logic against an incomplete source archive; (2) the new orchestrator event handler and its polling helpers fetch from the database without the getAuthorization()->skip() wrappers that every other read in the same worker uses, so they may silently return empty documents under the project authorization context; (3) the ?? '' null coalesce was dropped when $outputDirectory was hoisted out of the closure, meaning a null is now passed to the executor when neither buildOutput nor outputDirectory is configured.

Builds.php (new orchestrator methods), Functions/Http/Deployments/Create.php, and Sites/Http/Deployments/Create.php need the most attention.

Security Review

  • Shared encryption/callback secret (Events/Create.php, Builds.php): The orchestrator HMAC callback secret falls back to _APP_OPENSSL_KEY_V1, the application-wide encryption key. If the callback secret is exposed (e.g., via orchestrator logs or network capture), the attacker also gains access to the primary encryption key used to protect stored credentials and other sensitive data. A dedicated, non-fallback secret should be required for the callback path.

Important Files Changed

Filename Overview
src/Appwrite/Platform/Modules/Functions/Workers/Builds.php Core build worker extended with 834 lines of orchestrator support; missing auth-skip wrappers on new DB fetches and a null-coalesce regression in the executor path.
src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php Condition for emitting deployment events changed from $chunksUploaded === $chunks to !$deployment->isEmpty(), which fires events on every chunk upload instead of only the final one.
src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php Same premature-event bug as Functions/Create.php — condition changed to always-true !$deployment->isEmpty().
src/Appwrite/Platform/Modules/Deployments/Http/Deployments/Events/Create.php New endpoint verifying orchestrator HMAC signatures; callback secret falls back to the global OpenSSL key, which conflates two separate secrets.
src/Appwrite/Platform/Modules/Deployments/Http/Deployments/Artifacts/Build/Action.php New chunked upload handler for build artifacts with idempotency via locking and cache; logic looks correct.
src/Appwrite/Platform/Modules/Deployments/Http/Deployments/Artifacts/Source/Get.php New endpoint streaming source artifacts to the orchestrator; auth and resource ownership checks look solid.
app/init/resources/request.php Token lookup changed from getParam to getQuery; new deploymentArtifacts token resolver added with correct format validation.
src/Appwrite/Platform/Modules/Functions/Http/Deployments/Status/Update.php Cancellation now appends a log line, cleans up artifact tokens, and routes cleanup to the orchestrator or executor depending on backend config.
src/Appwrite/Platform/Modules/Sites/Http/Deployments/Status/Update.php Mirror of Functions Status/Update.php; same cancellation improvements applied correctly to the sites deployment path.

Comments Outside Diff (1)

  1. src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php, line 369-373 (link)

    P1 Events fire on every chunk, not just the final upload. $deployment is always non-empty at this point (it was just created or updated a few lines above), so the condition is effectively always-true. The original guard $chunksUploaded === $chunks existed to prevent the event — which triggers downstream build logic — from firing for incomplete, partial-chunk uploads. Removing it means the event will fire after chunk 1 of N, potentially starting a build before the source archive is complete. The identical change in Sites/Http/Deployments/Create.php line 441 has the same problem.

Reviews (1): Last reviewed commit: "feat: orchestrator build backend with De..." | Re-trigger Greptile

Comment on lines +1508 to +1513
$deployment = $dbForProject->getDocument('deployments', $deployment->getId());
if ($deployment->isEmpty()) {
throw new \Exception('Deployment not found');
}

$resource = $dbForProject->getDocument($resource->getCollection(), $resource->getId());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Missing getAuthorization()->skip() wrappers on database fetches. Every getDocument call in the existing executeBuild method that reads deployments or resources uses $dbForProject->getAuthorization()->skip(fn () => ...), because the project-level authorization scope must be bypassed in a worker context. The two fetches here — $deployment and $resource — are bare getDocument calls, inconsistent with that established pattern. The same inconsistency appears in waitForOrchestratorBuildPath (bare find) and waitForOrchestratorSiteDetectionLogs (bare getDocument). If authorization is active in the worker, these calls will silently return empty documents, causing the handler to bail out as though the deployment or resource were deleted.

Comment on lines +792 to 795
outputDirectory: $outputDirectory
);

} catch (ExecutorTimeout $error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 The null-coalescing guard ?? '' was removed when the $outputDirectory assignment was hoisted out of the closure. $deployment->getAttribute('buildOutput') and $resource->getAttribute('outputDirectory') can both return null, leaving $outputDirectory as null when neither attribute is set. Previously the ?? '' at the call site prevented null from reaching the executor. If the executor library's createRuntime has a non-nullable string type for outputDirectory, this becomes a TypeError on sites or functions that don't configure an output directory.

Suggested change
outputDirectory: $outputDirectory
);
} catch (ExecutorTimeout $error) {
outputDirectory: $outputDirectory ?? ''
);
} catch (ExecutorTimeout $error) {

BuildPublisher $publisherForBuilds
) {
$body = $request->getRawPayload();
$secret = System::getEnv('_APP_ORCHESTRATOR_CALLBACK_SECRET', System::getEnv('_APP_OPENSSL_KEY_V1', ''));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 security The orchestrator callback secret falls back to _APP_OPENSSL_KEY_V1, which is the application-wide encryption key used to protect sensitive stored data. If this fallback is taken in production (because the dedicated env var is not set), the same key signs outbound callbacks AND encrypts sensitive data at rest. A leak of the callback secret — e.g., via an attacker who can read orchestrator logs — would also compromise the encryption key. Deploying a separate secret via _APP_ORCHESTRATOR_CALLBACK_SECRET should be enforced, or the fallback should use a distinct derived value.

Suggested change
$secret = System::getEnv('_APP_ORCHESTRATOR_CALLBACK_SECRET', System::getEnv('_APP_OPENSSL_KEY_V1', ''));
$secret = System::getEnv('_APP_ORCHESTRATOR_CALLBACK_SECRET', '');
if (empty($secret)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Orchestrator callback secret is not configured.');
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant