# Inspect an upload and save a project plan

Source: https://docs.imprezahost.com/project-plans.html

Inspect a retained project archive before creating an app. The plan records the source version, its SHA256, selected project folder, findings and available build options. Choose a build option, save the server and runtime configuration for review, then confirm that saved deployment.

## Git inspection

Inspect a pinned Git commit through the portal, REST or local MCP 0.34.0+. Retained uploads remain supported.

Inspection accepts either `context_id` or `git_url` with a complete 40-character `git_commit`. In My Apps, choose **Git commit over HTTPS** in the inspection form. For a private repository, supply `git_username` and `git_token`; the token is used only for the fetch and is not saved in the plan or deployment. Use the same fields with `impreza_plan_project` or `POST /v1/platform/deployments/custom/plans`.

```
{
  "git_url": "https://github.com/OWNER/PROJECT.git",
  "git_commit": "0123456789abcdef0123456789abcdef01234567",
  "project_dir": "."
}
```

Review `source.origin.url`, `source.origin.commit` and `source.sha256` before selecting a build option. The exact captured archive is retained under the account upload quota. Preparing and applying the plan use those captured bytes, not a moving branch and not a second repository fetch. To change the source, inspect a new commit.

Only public-network HTTPS origins on port 443 are supported. Redirects, URL credentials and submodules are refused. No checkout, project hooks, dependency installation or application code runs during inspection. Git archive attributes can exclude files, and Git LFS objects are not materialized. Existing archive limits still apply, including rejection of links. Each Git command has a 60-second timeout, resource limits and bounded output. Errors include `UNSAFE_GIT_SOURCE`, `GIT_INSPECTION_FAILED`, `UNSUPPORTED_GIT_SOURCE` and `SOURCE_UNAVAILABLE`; quota and ownership checks still apply. Successful inspection is not proof that the application builds.

## npm workspaces

Use `npm_workspace` in project inspection and custom deployments through the portal, REST or local MCP 0.34.0+.

For an npm monorepo, set `project_dir` to the folder containing the root `package.json` and matching `package-lock.json`, then select a declared workspace such as `apps/web`. Leave the field absent for an independent package. A saved plan fixes both fields. Previews and redeploys inherit the saved selection.

```
{"context_id":"ctx_...","project_dir":".","npm_workspace":"apps/web"}
```

The root must declare an array of workspace paths. Exact paths and whole-segment wildcards, such as `packages/*`, are supported; recursive globs, negation and object declarations are refused. npm installs from the root lockfile, builds workspaces with scripts in declaration order, and starts the selected app with `npm start --workspace=apps/web`. Put shared packages before their consumers. This is not automatic dependency-order scheduling. Root build/start scripts are not run.

For `node_npm_static`, `static_output_dir` is relative to the selected workspace; only its generated site is copied into the web-server image. The combined workspace/output path must fit the existing five-segment, 120-character limit. Node server images retain the npm root and sibling packages. Do not place unrelated secrets in the source. Existing exclusions and symlink refusals apply. Public build variables reach workspace build scripts only; they are never a credential channel.

Inspection checks the selected package and root lockfile without running code. Undeclared selections produce `npm_workspace_not_declared` and no npm build option. Builds also recheck workspace membership. Missing paths, invalid lockfiles or failed shared builds fail the deployment; verify the operation result and health of the serving release. For standalone pnpm/Yarn projects and private registries, use the respective settings below.

## Pinned pnpm and Yarn

Local MCP 0.34.0+ supports `node_package_manager` for standalone Node server/static recipes in the portal, REST and MCP. Omit it to retain npm. Use an exact pin such as `pnpm@10.26.1` or `yarn@4.9.2`, also declared identically in the selected folder's `package.json`. Supported major families are pnpm 10–12 and Yarn 4; ranges, tags, classic Yarn and manager URLs are rejected.

Keep `pnpm-lock.yaml` or `yarn.lock` beside that package file. Inspection infers the pin without executing code; saved plans preserve it. Creation requires deploy scope. Installation uses a frozen/immutable lockfile and verifies its bytes after installation. Node server recipes remove development dependencies before startup; static recipes copy only the configured output into Nginx. Node 24 and the selected manager's dependency/build-script rules apply.

Pinned pnpm/Yarn recipes support standalone projects only: do not combine the field with `npm_workspace`, package workspaces or `pnpm-workspace.yaml`. Yarn uses the node-modules linker; source `.yarnrc`, `.yarnrc.yml`, `.yarn` and PnP files are excluded. Custom plugins, patched dependencies and additional manager configuration require a reviewed Dockerfile. Verify the deployment result and application health; an accepted plan is not proof that dependencies or scripts will build.

For private registries, the recipes accept `build_secrets.npmrc` with pnpm, or `build_secrets.yarnrc` containing Yarn YAML configuration. Yarn mounts it at `/app/.yarnrc.yml` for installation only; pnpm receives its temporary npm configuration file. Other secrets remain build-script-only. Use registry-host-scoped credentials, an agent with `build-secrets-v1`, and the explicit rotation workflow below. These settings require trusted project code and are never inherited automatically by previews.

## Build credentials

Build credentials require agent 0.6.11+ advertising `build-secrets-v1` and local MCP 0.34.0+ when using the local connector. Unsupported agents refuse the deployment before receiving the command.

Supply `build_secrets` as a JSON object when creating a Dockerfile/npm deployment or preparing a saved plan. In My Apps, use **Build secrets**. Each name starts with a letter and contains only ASCII letters, digits or underscores (64 characters maximum). Up to 16 nonempty UTF-8 values, 64 KiB each and 256 KiB total; no NUL characters. Do not use Public build variables for credentials.

The control plane encrypts values separately, bound to account, agent and app. Reviews and deployment reads show only `build_secret_names`. The assigned agent obtains values only for its active, authorized preparation operation. Manifests and queued jobs contain names and temporary file references, not values. Files are private to the agent state and removed after build completion or a handled failure. An interrupted operation still needs recovery verification before it can be declared clean.

For generated npm recipes, the name `npmrc` supplies an npm configuration file during dependency installation only. Scope registry tokens to their registry host. Other names are mounted under `/run/secrets/NAME` during build scripts only. They do not become runtime environment variables. For a custom Dockerfile, request each secret explicitly:

```
RUN --mount=type=secret,id=PackageToken,required=true \
    your-build-command --token-file /run/secrets/PackageToken
```

Build output is withheld when credentials are mounted. These builds bypass cached layers so replacement credentials are checked on the next build. Use trusted source and dependencies: a script can deliberately copy credentials into an image or send them over the network. Secret mounts do not prevent that. Avoid embedding values in commands or generated artifacts. Automatic previews do not inherit build credentials; create a separately reviewed preview app with explicitly scoped test credentials.

To replace expiring credentials, open the app card's **Build credentials**, or call `impreza_rotate_build_secrets` with `deployment_id` and `build_secrets`. REST uses `POST /v1/platform/deployments/custom/{id}/build-secrets` with deploy scope. Supply exactly the current names. Rotation is refused while an operation is pending/running, does not return values and does not redeploy. Start a redeploy explicitly when ready. Changing mount names requires a new reviewed deployment.

Verify the operation result, application response and health. Credential delivery failures fail before replacement. Generated Python/PHP recipes do not yet accept these fields.

## From My Apps

1.  Open the custom-app form and [upload a source version](/source-uploads.html). Remove credentials and local dependencies before uploading.
2.  Expand **Inspect an upload and save a plan**. Choose the archive and project folder (`.` by default). A Dockerfile path is relative to the archive root. The PHP public folder is relative to the project folder. Python needs an explicit production start command.
3.  Select **Inspect and save plan**. Review the detected folders, findings and build options. Missing or invalid lockfiles block npm/Composer recipes; a missing PHP public `index.php` also blocks that recipe. A valid JSON lockfile is not proof that it matches the dependency declaration.
4.  Choose an available option. The form fixes source and build settings to that plan. Review the app name, server, container port, domain, runtime variables, volumes and startup health policy before submitting.
5.  Select **Review and save deployment**. Read the immutable configuration in **Review saved deployments**, then select **Confirm and deploy**. Preparation creates no app or DNS record. A retry of the same saved deployment returns its original receipt.
6.  Use **Load saved plans** to resume an inspection, or **Load saved deployments** to resume a final configuration review. Clear the selected plan to edit source/build settings manually, or inspect again to save different settings. A new upload is required to change source bytes.

A source inspection plan can offer several build options. A prepared deployment fixes one option and its runtime configuration. Changes in the form require a new review; they do not alter a saved configuration. To update an existing app, use its normal [redeploy workflow](/source-uploads.html#mcp).

## Review once, apply safely

A prepared deployment records the source, recipe, project folder, start command or public folder, server, proposed domain, environment values, CPU/memory limits, container port, volumes and startup policy. It stores the generated recipe and checks the effective configuration again before applying. Environment values are encrypted in the saved record; review/list responses show their names only. Values reach the existing deployment configuration when applied. Never put credentials in names, domains or start commands.

Review the returned `execution_id` and `configuration_digest` with the configuration. Applying accepts those identifiers only; settings cannot be overridden. The app, queue job and acceptance receipt are committed together. Concurrent requests use the same receipt, or return a conflict while the first request finishes. After a lost response, retry the same identifiers. A committed receipt remains valid for replay after expiry, rule changes or app removal and never creates another job. Acceptance means queued, not healthy: inspect the app status and logs.

Before the first acceptance, expiry, changed rules, altered source bytes, an unavailable agent, a changed server IP or changed effective settings require revalidation or a fresh preparation. A failed transaction leaves no new app/job. A prepared deployment cannot restart a failed deployment; use the normal app workflow. Preparing a second record creates a separate intention, so reuse the original identifier when retrying.

At most 20 pending prepared deployments per account; each expires no later than its source plan and within 24 hours. Expired pending records are removed when the account prepares again. The encrypted preparation copy is removed on acceptance; the existing deployment still holds its runtime environment. Accepted receipts are retained. `ready_for_review` reflects the saved record, not live capacity or source availability.

Preparation reserves no capacity, domain, host port or build slot. A managed hostname can be proposed without creating DNS; it must still be available at apply. App IDs, host ports and onion addresses are allocated at execution. DNS is external to the database transaction: an interrupted attempt can leave a DNS record, even if no app/job committed. This flow does not pin external dependencies/images, promote artifacts or guarantee a successful build.

## From MCP

Local MCP requires **0.32.0+** for the prepare/apply flow (source inspection remains available in 0.31.0+). Hosted MCP uses existing account uploads and cannot read local folders. Both transports expose the same plan and deployment rules.

1.  Upload with `impreza_upload_context` in local MCP, or use an existing retained `context_id` from `impreza_list_contexts`.
2.  Call `impreza_plan_project` with `context_id` and optional `project_dir`, `dockerfile_path`, `start_command` and `php_document_root`.
3.  Read the findings and `analysis.deployment_options`. No options means the selected paths/configuration have no supported candidate. Reinspect after correcting the source or settings.
4.  After customer review, call `impreza_prepare_project_deployment` with `plan_id`, the zero-based `option_index`, `name` and `agent_id`, plus runtime settings. Do not resend source or build settings.
5.  Review the returned configuration, then call `impreza_apply_project_deployment` with only `execution_id` and `configuration_digest`. Use `impreza_list_prepared_deployments` to list records or retrieve one execution ID.
6.  Inspect the deployment status, logs and readiness. `impreza_list_project_plans` lists the latest 20 plans or retrieves one `plan_id`.

```
// Inspect an already uploaded Python project
{"context_id":"ctx_...","project_dir":"apps/api",
 "start_command":"exec uvicorn app:app --host 0.0.0.0 --port 8000"}

// Save configuration for review with impreza_prepare_project_deployment
{"plan_id":"plan_...","option_index":0,"name":"my-api",
 "agent_id":"agt_...","target_port":8000,
 "healthcheck_path":"/ready","require_healthy_start":true}

// After review: impreza_apply_project_deployment
{"execution_id":"pdep_...","configuration_digest":"returned digest"}
```

Start commands and plan findings are public application configuration within your account; never put credentials in them. Supply runtime secrets through the dedicated environment variables when preparing, deploying or editing an app.

## REST and lifecycle

*   `POST /v1/platform/deployments/custom/plans`: create an inspection plan, deploy scope, HTTP 201.
*   `GET /v1/platform/deployments/custom/plans`: latest 20 account plans, read scope.
*   `GET /v1/platform/deployments/custom/plans/{plan_id}`: retrieve one plan, read scope.
*   `POST /v1/platform/deployments/custom/plans/{plan_id}/prepare-deployment`: save final configuration, deploy scope, HTTP 201.
*   `GET /v1/platform/deployments/custom/prepared` and `/{execution_id}`: list/retrieve prepared deployments, read scope.
*   `POST /v1/platform/deployments/custom/prepared/{execution_id}/apply`: apply with `configuration_digest`, deploy scope; HTTP 201 on first acceptance, 200 on replay. Returns `receipt` with deployment/command IDs and `replayed`.
*   `POST /v1/platform/deployments/custom/plans/{plan_id}/deploy`: compatible direct creation from an option, deploy scope, HTTP 201. This older endpoint and `impreza_deploy_project_plan` remain reusable and are not idempotent. Use prepare/apply for safe retries.

A plan contains `plan_id`, `status`, creation/expiry timestamps, `source` (context ID, SHA256, size), `inspection_revision`, `project_dir`, an inventory summary and `analysis`. It does not return archive contents or storage paths. App metadata includes `project_plan` provenance for the creation request; it does not certify the currently running release after later redeploys or recovery.

Plans expire after at most 24 hours, earlier if the unused source expires. At most 20 unexpired plans are stored per account. Expired records are removed when that account creates another plan. The list can include expired records until then. A plan does not extend upload retention: deleting or losing the source makes it unavailable. Another account's plan returns 404. Resource-confined MCP credentials cannot access account plans.

`needs_review` means the saved plan can be considered, not that its build will succeed. `expired`, `stale` (inspection rules changed), or `source_unavailable` requires a fresh inspection. Execution rechecks expiry, rules, actual source SHA256 and account ownership. It also requires an online target heartbeat within 90 seconds, rejects an explicitly requested memory limit above reported total memory, and runs normal deployment ownership, domain and agent capability checks. These checks do not reserve free memory, CPU, domains or build slots.

Preparation and the older direct-deploy endpoint accept `option_index`, `name`, `agent_id`, `domain`, `vars`, `cpus`, `memory_mb`, `target_port`, `volumes`, `onion`, `build_secrets`, `healthcheck_path`, `require_healthy_start` and `startup_timeout_seconds`. Other fields are refused. Recipe defaults apply where omitted. Static plans use the standard `dist` output with SPA fallback; custom static output/routing or public build variables need the normal custom-deploy flow.

## Inspection boundaries

The inspector streams one gzip-compressed ustar/POSIX pax archive, with at most 256 MiB expanded data, 10,000 entries and ten seconds of processing. It rejects unsafe paths, duplicate entries, file/directory conflicts, links, devices, sparse files, GNU long-name records and unsupported pax attributes. Standard directory entries and safe pax paths are supported. If necessary, repack as ustar or POSIX pax without links or extended attributes.

It inventories paths and reads only the selected `package.json`, `requirements.txt`, `composer.json` and Dockerfile (32 KiB each), plus selected npm/Composer lockfiles (2 MiB each). Up to 50 candidate project folders are returned. Common sensitive filenames produce a finding, not a secret scan. No project code is executed and no archive is extracted.

Source SHA256 fixes uploaded bytes. Inspection can capture a pinned Git commit but does not analyze arbitrary source code, resolve dependencies, prove lockfile consistency, interpret ignore rules, pin external images/packages, discover all required environment variables or certify startup. Build options are candidates under the existing [recipe limits](/tutorials/agent-apps-panels.html). The source inspection plan alone does not freeze runtime settings; the optional prepared deployment does. Use [deployment history and startup checks](/deployment-safety.html) to verify the resulting app.
