diff --git a/docs/development/testing.md b/docs/development/testing.md index a353f2c1..a7b38964 100644 --- a/docs/development/testing.md +++ b/docs/development/testing.md @@ -3,6 +3,149 @@ This guide covers contributor-facing integration tests that are intentionally separate from the managed installation documentation. +## Managed build environment deployed-stack test + +Use a disposable AWS account or logical environment for this test. Managed +environment image builds use CodeBuild, ECR scanning, S3, DynamoDB, Lambda, +EventBridge, and Bedrock AgentCore resources that incur charges until they are +removed. + +Start from a checkout with AWS credentials for the test account. Create the +backend and variable files if this logical environment does not already exist. +The example below uses `managed-env-demo`; substitute your configured name +consistently: + +```bash +export AIDLC_TEST_ENV=managed-env-demo +./scripts/bootstrap.sh "$AIDLC_TEST_ENV" +cp terraform/environments/dev.tfvars.example \ + "terraform/environments/$AIDLC_TEST_ENV.tfvars" +``` + +Set `environment` and `aws_region` in the new tfvars file, then deploy the +infrastructure and frontend: + +```bash +./scripts/deploy-terraform.sh "$AIDLC_TEST_ENV" +./scripts/deploy-frontend.sh "$AIDLC_TEST_ENV" +``` + +The following outputs identify the managed environment resources used during +diagnosis: + +```bash +terraform -chdir=terraform output -raw environment_registry_table_name +terraform -chdir=terraform output -raw managed_environment_repository_name +terraform -chdir=terraform output -raw managed_environment_codebuild_project_name +terraform -chdir=terraform output -raw managed_environment_control_lambda_name +terraform -chdir=terraform output -raw managed_environment_status_lambda_name +terraform -chdir=terraform output -raw managed_environment_build_context_bucket_name +terraform -chdir=terraform output -raw managed_tool_repository_name +terraform -chdir=terraform output -raw managed_tool_codebuild_project_name +terraform -chdir=terraform output -raw managed_tool_control_lambda_name +terraform -chdir=terraform output -raw managed_tool_status_lambda_name +``` + +Sign in as a platform administrator and open **Platform Settings -> +Environments**. The deployment publishes only Standard. Within five minutes, +the catalog bootstrap creates Java, Go, Rust, Maven, and Gradle tool families +and queues their initial versions for import. + +This test covers three distinct paths: + +1. **Protected baseline:** Standard supplies Node.js and Python without catalog + imports. +2. **Shipped catalog tools:** Java, Go, Rust, Maven, and Gradle exercise the + bootstrap and import path. +3. **Administrator-created tools:** `.NET SDK` is a representative custom tool + used to exercise tool creation and presets. It is not otherwise privileged + or required by the platform. + +### Verify shipped catalog tools + +For each shipped tool: + +1. Follow its CodeBuild log. +2. Confirm the source URL, retained source digest, publisher evidence, OCI + digest, SBOM, compressed size, and core compatibility evidence are visible. +3. Confirm the version command and representative build run as the non-root + runtime user. +4. Review ECR findings. Accept Critical or High findings only for this + disposable deployment. If ECR reports the normalized artifact as + unsupported, explicitly accept that scan limitation and confirm the + acceptance remains visible. +5. Publish the version. Mark Java as recommended before publishing Maven or + Gradle. + +### Verify custom tool creation + +Create a `.NET SDK` tool using an official Linux ARM64 SDK archive and the +`.NET` preset. Confirm source inspection, normalization, scanning, `dotnet +--version`, and a real console build succeed, then publish it. + +### Verify environment composition + +Create and publish the following environments based on Standard: + +| Environment | Selected tools | Path under test | +| ----------- | ---------------------------------------------- | -------------------------- | +| Go | Go | A standalone shipped tool | +| Maven | Maven; recommended Java is added automatically | Tool dependency resolution | +| .NET | The administrator-created `.NET SDK` | Custom tool composition | + +For each environment, confirm the generated Dockerfile copies tools from exact +OCI digests and retains the protected base entrypoint, command, user, port, and +health behavior. Confirm the projected and actual compressed image sizes stay +below the configured AgentCore image limit. + +Create projects with small repositories that exercise each selected toolchain. +In **Project Settings -> Environment**, assign each published environment and +start a new intent. Confirm the intent detail and audit views show the exact +environment revision, image digest, runtime version, endpoint, compatibility +version, tool snapshots, and passed verification result. + +To verify immutable intent targeting: + +1. Start an intent and record its environment revision and runtime endpoint. +2. Change the project's environment assignment. +3. Resume, rewind, cancel, and stop the original intent. +4. Confirm its detail and audit views retain the original revision and + endpoint, while a newly created intent uses the new assignment. + +To verify updates: + +1. Publish a second Go tool version and leave the original recommended. +2. Confirm existing environments remain unchanged. +3. Mark the new Go version recommended and confirm affected environments show a + structured tool update warning. +4. Edit one affected environment to select the new exact version, then build + and publish it. +5. Publish a new Standard revision and confirm dependent environments retain + their published revisions until **Rebuild on latest base** is used. + +Inspect failures through the UI or the resource outputs above. Critical and +High findings must stop at security review until a platform administrator +accepts them. The findings and acceptance record must remain visible after +publication. Image build, container validation, and AgentCore endpoint failures +must leave the previous published revision and project assignments unchanged. + +When testing is complete, delete the test intents and retire the catalog-backed +test environments in the UI. Environment and tool images are retained while +the stack exists; the non-production repositories are force-deleted with the +stack. Managed AgentCore runtimes and endpoints are created by the control +plane rather than Terraform, so delete remaining test endpoints and runtimes +in the AgentCore console before destroying the logical deployment. Use the +revision details in **Platform Settings -> Environments** to identify the +runtime ID, version, and endpoint. The resources are also tagged with +`ManagedEnvironment` and `ManagedEnvironmentRevision`. + +After those resources are removed, destroy the logical deployment: + +```bash +./scripts/destroy.sh "$AIDLC_TEST_ENV" +unset AIDLC_TEST_ENV +``` + ## Enterprise SSO integration test The repository includes a disposable Cognito User Pool that behaves as an diff --git a/docs/using-the-platform/creating-intents.md b/docs/using-the-platform/creating-intents.md index 2453b7f1..3e4e9c34 100644 --- a/docs/using-the-platform/creating-intents.md +++ b/docs/using-the-platform/creating-intents.md @@ -43,10 +43,16 @@ Only repositories where you explicitly picked a branch are overridden; all other Creating the intent opens it on the workbench in **DRAFT** state, with a **Review & start** card showing the prompt, scope, and branch (read-only — these are set at creation). Starting the intent: -1. Pins the current workflow version and snapshots the project's runtime settings. +1. Uses the workflow, runtime settings, and exact managed-environment revision + snapshotted when the intent was created. 2. Compiles the execution plan for the chosen scope. 3. Checks the repositories out, creates the intent branch, and begins the first stage. +Select the desired published environment in **Project Settings -> Environment** +before creating the intent. Reassigning the project or publishing a newer +revision afterward does not change an existing intent's image, runtime, or +endpoint. See [Managed tools and environments](managed-environments.md#select-an-environment-for-a-project). + ## Intent lifecycle | State | Meaning | diff --git a/docs/using-the-platform/intent-observability.md b/docs/using-the-platform/intent-observability.md index d03f3190..3ee51279 100644 --- a/docs/using-the-platform/intent-observability.md +++ b/docs/using-the-platform/intent-observability.md @@ -61,7 +61,14 @@ Selecting a node shares the same drill-down as the observability page. ## The audit page -The **Audit** button opens the graph-usage audit: how agents read the graph (compact reads vs. full documents), enrichment spend, derivation health, structure-contract compliance, and coverage findings (for example, must-have requirements not covered by any story). It is the measurement surface for tuning context efficiency. +The **Audit** button opens the graph-usage audit: how agents read the graph +(compact reads vs. full documents), enrichment spend, derivation health, +structure-contract compliance, and coverage findings (for example, must-have +requirements not covered by any story). It also records the exact managed +environment revision, image digest, runtime version, endpoint, compatibility +version, and verification result used by the intent. It is the measurement +surface for tuning context efficiency and confirming which immutable runtime +executed a run. ## Project-level metrics diff --git a/docs/using-the-platform/managed-environments.md b/docs/using-the-platform/managed-environments.md new file mode 100644 index 00000000..21790309 --- /dev/null +++ b/docs/using-the-platform/managed-environments.md @@ -0,0 +1,623 @@ +# Managed tools and environments + +Platform administrators control the software available inside intent runtimes. +The platform ships one protected **Standard** environment and a catalog of tool +definitions. Administrators publish exact tool versions, compose those versions +into environments, and publish environment revisions that project owners can +assign. + +Standard provides the protected AgentCore runtime plus Node.js and Python. +Java, Go, Rust, Maven, and Gradle are shipped as tool definitions, not as +predefined JVM, Go, Rust, or Polyglot environments. Administrators can add other +tools, such as a .NET SDK, without changing the application source. + +## Quick start + +Choose the path that matches your task: + +| Goal | What to do | +| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Use the built-in Node.js and Python runtime | Use **Standard**. Projects use it automatically until another environment is assigned. | +| Create a Java, Go, Rust, Maven, or Gradle runtime | [Publish the required shipped tool versions](#publish-and-recommend-tools), [create an environment](#create-an-environment), then [publish it](#publish-an-environment). | +| Add another SDK or CLI | [Add and verify a tool](#add-a-new-tool), publish its exact version, then include it in an environment. | +| Upgrade a tool without changing existing runs | [Add the new tool version](#add-or-update-a-tool-version), create and publish a new environment revision, then create new intents. | +| Make an environment available to a project | [Assign the published environment](#select-an-environment-for-a-project) before creating the intent. | +| Diagnose a failed or blocked build | Review [tool failures and security findings](#review-failures-and-security-findings) or [environment build evidence](#what-an-environment-build-verifies). | + +The normal flow is: + +```mermaid +flowchart LR + T["Publish exact tool versions"] --> E["Build an environment revision"] + S["Standard
Node.js + Python"] --> E + E --> P["Publish the environment"] + P --> A["Assign it to a project"] + A --> I["Create an intent
runtime snapshot is pinned"] +``` + +Publishing a newer tool or environment revision never changes an existing +intent. New intents snapshot the currently published revision of the +project-assigned environment. + +## Detailed guide + +### Access and responsibilities + +The managed build views are under **Admin -> Environments** and require the +`platform-admin` role: + +- **Tools** imports, builds, verifies, publishes, and recommends tool versions. +- **Environments** composes published tool versions into runtime images. + +Project Owners and Admins select a published environment under **Project +Settings -> Environment**. Project Members can see the assignment but cannot +change it. + +### The object model + +| Object | Meaning | +| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| **Standard** | The protected AgentCore base, Node.js, Python, runtime files, user, entrypoint, port, and health behavior. | +| **Tool family** | A stable capability such as `java`, `go`, `rust`, `maven`, `gradle`, or `dotnet-sdk`. | +| **Tool version** | An immutable, verified ARM64 tool artifact for one exact version. | +| **Environment** | A named toolchain based on one published environment. | +| **Environment revision** | An immutable image recipe and runtime created from exact tool-version and base-revision snapshots. | +| **Project assignment** | The environment whose current published revision will be used by new intents. | +| **Intent snapshot** | The exact environment revision, image digest, runtime target, compatibility version, and verification result captured when an intent is created. | + +The important invariants are: + +- Only published tool versions can be added to an environment. +- Only a `READY` tool version or environment revision can be published. +- Published tool versions and environment revisions are immutable. +- Publishing or recommending a tool version never rewrites an existing + environment revision. +- Publishing a new environment revision affects new intents for assigned + projects. Existing intents stay pinned to their original runtime target. + +### Shipped tool definitions + +The catalog bootstrap creates these tool families and initial version +definitions: + +| Tool | Initial version | Dependency | +| -------------- | --------------- | ---------------- | +| Java JDK | `21.0.8` | None | +| Go SDK | `1.24.6` | None | +| Rust Toolchain | `1.89.0` | None | +| Apache Maven | `3.9.11` | Recommended Java | +| Gradle | `9.0.0` | Recommended Java | + +The scheduled bootstrap queues independent initial versions for import. Maven +and Gradle remain drafts until Java has a published recommended version because +their own functional checks require Java. + +These are catalog entries only. They do not create project-selectable +environments by themselves. + +### Tool-version lifecycle + +The version selector in **Tools** shows every version and its status: + +| Status | Meaning | Available action | +| ----------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | +| `DRAFT` | Definition is editable and no build is active. | **Edit** or **Build** | +| `QUEUED` | CodeBuild has been requested. | Wait or refresh | +| `BUILDING` | Source import, installation, normalization, and functional checks are running. | Open **Build logs** | +| `SCANNING` | The immutable OCI artifact exists and ECR scan results are being evaluated. | Wait or refresh | +| `SECURITY_REVIEW` | Critical or High findings, or an unsupported artifact scan, require an explicit administrator decision. | **Accept Findings** or **Accept Scan Limitation** | +| `READY` | Source, artifact, scan decision, and verification evidence are complete. | **Publish** | +| `PUBLISHED` | The immutable version can be selected by environments. | **Recommend** when it is not already recommended | +| `FAILED` | Import, installation, scan processing, or verification failed. | Inspect evidence, **Edit**, or **Retry** | + +Refreshing the browser is not required while a build is active; the view polls +the version until it reaches a reviewable or terminal status. + +### Add a new tool + +#### Prepare the source + +Before opening the form, identify: + +- An exact version. +- An official Linux ARM64 archive URL. +- The executable paths inside the installed tool. +- A command that prints the version and a stable expected substring. +- A representative functional check. +- Any exact Debian package prerequisites. +- Any dependency on another tool family. +- An optional publisher checksum and public checksum-evidence URL. + +The normal import path accepts public HTTPS archives ending in: + +- `.tar.gz` +- `.tgz` +- `.tar.xz` +- `.zip` + +The source URL must not contain credentials, query parameters, fragments, or +embedded secrets. The platform rejects private, local, link-local, and +metadata-service destinations. The source download is bounded, redirects are +revalidated, and the archive is checked for traversal paths, unsafe links, +special files, excessive entry counts, and excessive expanded size. + +Source uploads are not supported. Tool definitions import from public HTTPS +URLs so the retained source has an auditable publisher location. + +Current import limits are: + +| Limit | Maximum | +| --------------------------------- | ---------- | +| Source archive download | `1024 MiB` | +| Archive entries | `200,000` | +| Expanded archive content | `4096 MiB` | +| Normalized tool output | `1536 MiB` | +| Final AgentCore environment image | `2048 MiB` | + +#### Create the family and first version + +1. Open **Admin -> Environments -> Tools**. +2. Choose **Add Tool**. +3. Enter a human-readable **Name**, for example `.NET SDK`. +4. Enter or accept the stable lowercase **ID**, for example `dotnet-sdk`. +5. Enter the **Publisher**, **Category**, and **Description**. +6. Enter the **Exact version**. +7. Select a **Verification** preset. +8. Enter the **Official ARM64 archive** URL. +9. Review the generated installation and verification defaults. +10. Choose **Create and Build**. + +Creating the family and creating its first version happen together. A tool +family can later contain multiple published versions. + +The tool ID is a durable catalog key. Do not include the version in it. Use +`dotnet-sdk`, not `dotnet-sdk-8`. The version belongs to the tool-version +record. + +#### Choose a verification preset + +Presets fill in executable paths, environment variables, dependencies, the +version command, and a representative build: + +| Preset | Functional verification | +| --------------- | ------------------------------------------------------------------------------------------------------ | +| **Java** | Runs `java -version`, compiles a class with `javac`, and runs it. | +| **Go** | Runs `go version`, builds a small Go program, and runs it. | +| **Rust** | Runs `rustc --version`, creates a Cargo project, builds it, and runs it. | +| **Maven** | Runs `mvn --version` and validates a minimal project offline. Adds Java as a dependency. | +| **Gradle** | Runs `gradle --version` and executes a minimal task offline. Adds Java as a dependency. | +| **.NET** | Runs `dotnet --version`, creates a console project, builds it, and runs it. | +| **Generic CLI** | Runs the configured version command. Add a custom verifier when a version check alone is insufficient. | + +Selecting a preset is a starting point, not a bypass. Review the generated +executable paths and expected version before building. + +#### Publisher checksum and source trust + +The publisher checksum section is optional. Supply both: + +- A SHA-256 or SHA-512 digest published by the vendor. +- A public evidence URL from which the platform can independently find that + digest. + +The catalog displays: + +- **Publisher verified** when the downloaded archive matches the supplied + publisher digest and the digest is also found at the independently fetched + evidence URL. +- **Platform pinned** when the platform computed and retained its own SHA-256 + digest but no independently verified publisher evidence was supplied. + +Both trust levels pin the imported source and resulting OCI artifact by digest. +Publisher verified adds independent evidence about who published the source. + +The first successful source import is retained under its digest. Retrying an +installer or verifier correction reuses that retained source instead of +silently downloading a new archive. + +#### Generated archive installation + +Use generated installation whenever the archive already contains the desired +tool layout. + +Set **Root folders to remove** to the number of wrapper directories that should +be removed during extraction. For example, an archive containing +`tool-1.2.3/bin/tool` needs one root folder removed when `bin/tool` should be +at the normalized tool root. + +Generated installation extracts the verified archive without executing +publisher-supplied installation code. + +#### Custom installation + +Enable the custom installer only when extraction is insufficient. The Bash +script receives: + +- `TOOL_SOURCE`: the read-only retained source archive. +- `TOOL_OUTPUT`: the writable directory that must contain the normalized tool. +- `TOOL_ARCHIVE_FORMAT`: the detected source format. + +The installer runs inside a nested container with: + +- No AWS credentials. +- No EC2 or ECS metadata access. +- No Docker socket. +- No host mounts other than the retained source and output directory. +- No access to private or link-local networks. +- Bounded CPU, memory, process count, temporary storage, and output size. + +Public internet access is available to the installer. If the script downloads +additional mutable content, the published OCI digest remains immutable and +verified, but rerunning the script is not guaranteed to produce the same +artifact. Prefer the retained source archive and exact checksums whenever +possible. + +Do not put credentials or secrets in installer scripts. The API rejects common +secret patterns, and definitions are visible to platform administrators. + +#### Executables and dependencies + +Under **Executables, dependencies, and custom verification**: + +- List each executable as `name=relative/path`, for example + `dotnet=dotnet` or `java=bin/java`. +- Select another tool family when this tool cannot be verified or used without + it. +- Add required Debian packages as `package=exact-version`. +- Add non-secret variables as `NAME=value`. +- Use `${TOOL_ROOT}` when a variable should point at the normalized tool root. + +A dependency resolves to that family's published recommended version. A +dependent version cannot build until every dependency has a published +recommended version. The platform rejects missing families, self-dependencies, +dependency cycles, executable collisions, package-version conflicts, and +environment-variable conflicts. + +#### Custom verification + +Every version requires a version command and expected output. Use the optional +networkless verifier for additional behavior that the selected preset does not +cover. + +The verifier: + +- Runs as the non-root runtime user. +- Has no network access. +- Uses isolated writable caches for SDKs and build tools. +- Can read up to 32 text fixture files supplied in the form. +- Receives fixture files read-only. +- Must complete within the build's resource and time limits. + +Use fixture files for a minimal real project, configuration file, or expected +output. Do not include credentials or production data. + +#### Example: add a .NET SDK + +1. Choose **Add Tool**. +2. Set **Name** to `.NET SDK` and **ID** to `dotnet-sdk`. +3. Set **Publisher** to `Microsoft` and **Category** to `Language SDK`. +4. Enter the exact SDK version. +5. Select the **.NET** preset. +6. Enter Microsoft's official Linux ARM64 SDK archive URL. +7. Add publisher checksum evidence when available. +8. Confirm the preset exposes `dotnet`, sets + `DOTNET_ROOT=${TOOL_ROOT}`, and expects the exact SDK version. +9. Choose **Create and Build**. +10. Review the source, OCI artifact, scan, and console-project verification. +11. Publish the version. +12. Recommend it if it should be the default .NET SDK for new environment + drafts. + +This adds .NET as a selectable tool. It does not create or publish a .NET +environment automatically. + +### What a tool build verifies + +A successful CodeBuild job is only one part of the tool decision. The catalog +records evidence for: + +1. **Source import**: validated public URL, resolved redirects, source size, + platform SHA-256, and optional publisher evidence. +2. **Archive safety**: bounded entry count and expanded size, safe relative + paths, safe links, and no special files. +3. **Installation**: generated extraction or sandboxed installer output. +4. **Artifact normalization**: executable paths exist, symlinks remain inside + the tool root, and output stays within the tool-artifact size limit. +5. **OCI publication**: an immutable ARM64 tool image and digest. +6. **SBOM**: an SPDX document generated from the normalized payload. +7. **Security scan**: ECR findings with severity, advisory, package, and + package version where available, or a recorded administrator decision when + ECR cannot scan the normalized artifact. +8. **Runtime compatibility**: the protected core digest and compatibility + version used for verification. +9. **Version check**: the configured command contains the exact expected + output. +10. **Functional check**: the selected preset and optional custom verifier run + as the non-root runtime user. + +The Tools view retains the source digest, trust level, official source link, +artifact digest, compressed size, runtime contract, findings, acceptance +identity, failure detail, and CodeBuild logs. + +### Review failures and security findings + +#### Build or verification failure + +For a `FAILED` version: + +1. Open **Build logs** and read the failure detail shown in the version. +2. Choose **Edit** when the archive layout, executable path, installer, + package, variable, version command, or verifier is wrong. +3. Choose **Save and Build** to store the corrected definition and queue a new + build. +4. Choose **Retry** only when the definition is already correct and the failure + was transient. + +The exact version string cannot be changed while editing. Create a new version +when the version number changes. + +#### Security review + +Critical or High ECR findings move the version to `SECURITY_REVIEW`; they do +not erase a successful artifact build. ECR Basic scanning cannot inspect some +normalized artifacts that intentionally contain no operating-system package +database. Those versions also move to `SECURITY_REVIEW` with the scanner +limitation retained as evidence. + +The administrator can: + +- Leave the version unpublished and remediate its source or dependencies. +- Choose **Accept Findings** to record the identity and timestamp and move the + version to `READY`. +- Choose **Accept Scan Limitation** when ECR reports the artifact as + unsupported, after reviewing the publisher checksum, generated SBOM, and + networkless functional verification. + +Acceptance does not publish the version. The findings and acceptance record +remain visible after publication and in every environment revision that +snapshots that tool version. + +### Publish and recommend tools + +Publishing and recommending are separate decisions: + +| Action | Effect | +| ------------- | ------------------------------------------------------------------------------------- | +| **Publish** | Makes one exact immutable version available for explicit environment selection. | +| **Recommend** | Makes one published version the default for new selections and dependency resolution. | + +Multiple versions of the same tool can remain published. Only one version is +recommended. + +Publishing a newer version does not automatically recommend it. Recommending a +new version: + +- Changes the version selected when an administrator first enables that tool + in an environment editor. +- Changes the dependency version automatically selected for tools such as + Maven or Gradle. +- Marks affected environments with a tool-update warning. +- Does not change an existing environment revision. +- Does not change an active or completed intent. + +For shipped tools, publish and recommend Java before building Maven or Gradle. + +### Add or update a tool version + +To add a newer version: + +1. Open the tool family in **Admin -> Environments -> Tools**. +2. Choose **Add Version**. +3. Enter the new exact version, official ARM64 archive, and verification + definition. +4. Choose **Create and Build**. +5. Review the complete evidence. +6. Publish the version. +7. Choose **Recommend** only when the new version should become the default. + +The prior published version remains selectable. Environments pinned to it +remain valid. + +To correct an unpublished version, use **Edit** while its status is `DRAFT` or +`FAILED`. Published versions cannot be edited; create another version instead. + +### Select a specific Java, Go, Rust, or other tool version + +Tool versions are selected on an environment revision, not globally: + +1. Publish every exact version that administrators should be able to use. +2. Open **Admin -> Environments -> Environments**. +3. Create an environment or open an existing environment. +4. Enable the tool when it is not inherited from the base. +5. Select the exact published version from the version control. +6. Choose **Create Draft** or **Save as New Revision**. +7. Build, review, and publish that environment revision. + +When a tool has only one published version, the editor shows its version badge +and an include switch; there is no redundant version selector. A selector +appears when: + +- More than one published version is available. +- The base already includes the tool and a published version can override the + inherited version. + +The recommended version is preselected when a tool is enabled. It is a default, +not a restriction. Any published version shown by the selector can be chosen. + +### Create an environment + +Open **Admin -> Environments -> Environments** and choose **New Environment**. + +1. Enter a stable environment ID, name, and description. +2. Select a published **Base environment**. Use Standard unless the new + environment intentionally extends another published custom environment. +3. Review the protected Node.js and Python versions and any tools inherited + from the base. +4. Enable catalog tools. The initial toggle selects the recommended published + version. +5. Use the version selector when multiple published versions exist or when + overriding a tool inherited from the base. +6. Review dependencies marked **Required**. They are added at their recommended + published versions. +7. Add exact apt packages, non-secret environment variables, or restricted + single-line build commands only when the catalog tools do not cover the + need. +8. Review the projected compressed size. AgentCore runtime images must remain + at or below `2048 MiB`. +9. Choose **Create Draft**. +10. Select the draft revision and choose **Build**. + +Environment variables and build commands cannot replace protected runtime +behavior, inject secrets, change the runtime user, entrypoint, command, port, +or health contract, or overwrite protected platform variables. + +### What an environment build verifies + +The generated Dockerfile: + +- Starts from the exact pinned base revision and digest. +- Copies each selected tool from its exact OCI digest. +- Installs exact apt package versions. +- Applies non-secret variables and restricted build commands. +- Restores the protected non-root runtime user. +- Retains the platform-owned runtime files, entrypoint, command, port, and + health behavior. + +The build then checks: + +- ARM64 architecture and the pinned base digest. +- Protected runtime files against the base. +- Non-root execution and writable workspace behavior. +- The generated SPDX SBOM. +- Tool version commands and representative builds. +- Container startup, `/ping`, shutdown, and runtime invariants. +- Final image size. +- ECR vulnerability findings. +- AgentCore runtime and endpoint creation. +- Capability and deterministic command checks through the created endpoint. + +The revision reaches `READY` only after image validation, the security decision, +and AgentCore runtime validation succeed. + +### Environment-revision lifecycle + +| Status | Meaning | Available action | +| ----------------- | ---------------------------------------------------------------- | ----------------------------------------------- | +| `DRAFT` | Editable recipe snapshot, not yet built. | **Build** | +| `QUEUED` | CodeBuild has been requested. | Wait or refresh | +| `BUILDING` | The composed image and local checks are running. | Open **Build logs** | +| `SCANNING` | ECR scan results are being evaluated. | Wait or refresh | +| `SECURITY_REVIEW` | Critical or High findings require a recorded decision. | **Accept Findings & Continue** | +| `VERIFYING` | AgentCore runtime and endpoint validation are running. | Wait or refresh | +| `READY` | All required checks passed or findings were explicitly accepted. | **Publish** | +| `PUBLISHED` | This is, or was, a published immutable revision. | Create another revision for changes | +| `SUPERSEDED` | A newer revision is now published. | Existing intent snapshots remain valid | +| `FAILED` | Image build, scan processing, or runtime validation failed. | Inspect evidence and **Retry** when appropriate | +| `RETIRED` | The environment no longer accepts changes or new assignments. | No restore action is currently available | + +The evidence view separates image-build success, security findings, and runtime +validation. A successful image can therefore remain visible even when it is +waiting for security review or later runtime validation fails. + +### Publish an environment + +Select a `READY` revision and choose **Publish**. + +Publication atomically moves the environment's published-revision pointer. The +previous published revision becomes superseded but remains retained while +active intent snapshots reference its runtime artifacts. + +After publication: + +- The environment appears in **Project Settings -> Environment**. +- New intents in projects already assigned to that environment use the newly + published revision. +- Existing intents continue using their snapshotted revision and endpoint. +- Draft, failed, and ready revisions do not affect project execution until one + is published. + +### Update an environment + +Environment revisions are immutable. Every change creates another draft +revision. + +#### Use a newer tool version + +1. Publish the new tool version. +2. Recommend it when it should become the default. Affected environments show + a recommended-tool update warning. +3. Open the environment. +4. Select the desired exact version in the catalog-tool list. +5. Choose **Save as New Revision**. +6. Build, review, and publish the new revision. + +The warning is informational. No revision changes until the administrator +selects versions and saves a new revision. + +#### Use a newer base revision + +When the published base changes, dependent environments show an update +warning. Choose **Rebuild on Latest Base** to clone the current recipe and +change only its pinned base revision. + +The rebuilt revision still requires image validation, security review when +applicable, runtime validation, and manual publication. + +When both base and tool updates are available, edit the environment and use +**Save as New Revision** so the exact tool selections are explicit. + +#### Retry a failed revision + +Use **Retry** only when the same recipe and pinned base are still valid. If a +newer base is required or the pinned digest is unavailable, use **Rebuild on +Latest Base** instead. + +A failed attempt never replaces the currently published revision and never +changes project assignments. + +### Select an environment for a project + +1. Open the project. +2. Open **Project Settings -> Environment**. +3. Select a published environment. +4. Review its included tools, exact published revision, image digest, and + runtime compatibility version. +5. Review repository compatibility warnings. +6. Choose **Assign Environment**. + +Compatibility warnings compare detected repository stacks with the tools in +the selected environment. They are advisory; assignment is still allowed. + +Projects without an explicit assignment use Standard. + +The assignment stores the environment ID, not a mutable container reference. +When an intent is created, the platform resolves that environment's current +published revision and snapshots: + +- Environment ID and name. +- Revision ID. +- Image digest. +- AgentCore runtime ARN and version. +- Runtime endpoint. +- Runtime compatibility version. +- Verification result. +- Resolved tool versions. + +Changing the project assignment affects only intents created afterward. +Running, waiting, rewound, cancelled, and resumed intents continue to target +their original snapshotted runtime and endpoint. + +Choose the environment before creating the intent that should use it. + +### Audit an intent's environment + +Intent details and audit output show the immutable environment snapshot used by +the run. Use those values when diagnosing a difference between two runs: + +- A project may have been reassigned after the older intent was created. +- A newer revision may have been published for the same environment ID. +- A tool recommendation may have changed without that tool version being + incorporated into the published environment. + +The project setting describes what the next intent will use. The intent +snapshot describes what that specific intent actually uses. diff --git a/docs/using-the-platform/platform-settings.md b/docs/using-the-platform/platform-settings.md index 4fb44722..0dafe422 100644 --- a/docs/using-the-platform/platform-settings.md +++ b/docs/using-the-platform/platform-settings.md @@ -2,7 +2,7 @@ The **Admin** page holds all platform-wide settings: users, agents, source control, and issue trackers. It is visible in the sidebar only to members of the Cognito **`platform-admin`** group (see [Setup](../getting-started/setup.md#bootstrap-the-first-platform-administrator) for bootstrapping the first admin); every underlying API is independently gated on the same group. -The page is organized into four tabs. +The page is organized into five tabs. A read-only **Deployment** strip sits above the tabs, showing the canonical application URL, environment, and region. The hostname it reports is the one the backend puts in its OAuth redirect URIs, so it is what every provider's callback URL must match. If you are browsing a hostname other than the canonical one — a deployment with a custom domain still answers on the CloudFront domain and on every alias — the strip says so, because the callback URLs shown further down deliberately use the canonical hostname rather than the one in the address bar. @@ -27,6 +27,26 @@ Everything the agent runtime needs to run: - **Default Models** — the platform-wide default model per CLI (Kiro, Claude Code, OpenCode, Codex), selected from a dropdown of models discovered from the runtime (or "No default — use CLI built-in"). Codex uses Bedrock's OpenAI models with exact `openai.*` IDs (e.g. `openai.gpt-5.5`) — the chosen model must be available in the deployment Region. Projects can override these per-CLI in [Project Settings → Agent](projects.md#agent). - **Graph Enrichment** — a switch controlling whether the platform adds LLM-generated summaries to derived artifacts in the knowledge graph (`llm` or `off`). The setting takes effect for the _next_ intent, never mid-run; enrichment spend is metered and surfaced on each intent's Audit page. +## Environments + +The environment area contains two administrator views: + +- **Environments** — compose exact published tool versions over a protected + base, build and scan the resulting ARM64 image, validate its AgentCore + runtime, and publish it for project assignment. +- **Tools** — import, verify, publish, and recommend immutable tool versions. + Java, Go, Rust, Maven, and Gradle are shipped as tool definitions rather than + predefined environments. Administrators can add other tools such as .NET. + +Only Standard is published as a system environment. Existing intents remain +pinned to their exact environment revision when assignments, bases, or +recommended tool versions change. + +See [Managed tools and environments](managed-environments.md) for the complete +administrator workflow: adding a tool, source provenance, build verification, +security review, publication and recommendation, environment composition and +updates, project assignment, and intent pinning. + ## Source Control Platform-wide code-host configuration: diff --git a/docs/using-the-platform/projects.md b/docs/using-the-platform/projects.md index b7d53ac0..8c194d90 100644 --- a/docs/using-the-platform/projects.md +++ b/docs/using-the-platform/projects.md @@ -5,6 +5,7 @@ A project is the workspace where intents run. It represents a product, service, - One or more **git repositories** (the code host backing the agents' workspace) - **Members** with project-level roles - **Tracker bindings** (GitHub Issues, GitLab Issues, Jira Cloud) +- A published **managed environment** for new intents - **Runtime settings** — which agent CLI runs, which models, how parallel construction behaves - The project's **intents** and their history @@ -63,6 +64,21 @@ separate identities that share an email address remain distinguishable. - **Agent CLI** — which headless CLI executes stages: **Kiro**, **Claude Code**, **OpenCode**, or **Codex** (OpenAI models on Bedrock). Availability reflects which credentials the operator has configured in [Platform Admin → Agents](platform-settings.md#agents). - **Model Override** — pin a specific model per CLI for this project. When unset, the platform-wide default model from **Admin → Agents → Default Models** applies. Project overrides take precedence over the stage/agent-level model hints in the workflow. +### Environment + +- **Environment** — select the published toolchain used by newly created + intents. The panel shows the exact published revision, image digest, runtime + compatibility version, and included tools. +- **Compatibility warnings** — detected repository stacks are compared with + the selected tools. Warnings are advisory and do not block assignment. +- **Standard fallback** — projects without an explicit assignment use the + protected Standard Node.js/Python environment. + +Choose **Assign Environment** before creating the intent that should use it. +Changing the assignment does not move an existing intent to another runtime. +See [Managed tools and environments](managed-environments.md#select-an-environment-for-a-project) +for publication, selection, and snapshot behavior. + ### Source Control - **Repositories** — add or remove the repositories that agents check out. Multi-repository projects are supported; at intent creation you can pick a base branch per repository. @@ -74,7 +90,10 @@ separate identities that share an email address remain distinguishable. ## Settings snapshots -Runtime-relevant settings (CLI, models, park release, max parallel units, PR strategy) are **snapshotted onto each intent when it is created**. Changing a setting affects the next intent, never a run already in flight. +Runtime-relevant settings (CLI, models, environment revision, park release, +max parallel units, and PR strategy) are **snapshotted onto each intent when it +is created**. Changing a setting affects the next intent, never a run already +in flight. ## Deleting diff --git a/frontend/src/components/admin/tabs/EnvironmentRegistry.tsx b/frontend/src/components/admin/tabs/EnvironmentRegistry.tsx new file mode 100644 index 00000000..53f015e7 --- /dev/null +++ b/frontend/src/components/admin/tabs/EnvironmentRegistry.tsx @@ -0,0 +1,1181 @@ +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { + Archive, + Boxes, + CircleCheck, + ExternalLink, + Hammer, + Loader2, + Plus, + RefreshCw, + Rocket, + RotateCw, + Save, + ShieldAlert, + ShieldCheck, + TriangleAlert, +} from 'lucide-react'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Skeleton } from '@/components/ui/skeleton'; +import { Switch } from '@/components/ui/switch'; +import { Textarea } from '@/components/ui/textarea'; +import { SettingsCard } from '@/components/settings/SettingsCard'; +import { + environmentsService, + toolsService, + type EnvironmentDetail, + type EnvironmentRecipeInput, + type EnvironmentRevision, + type EnvironmentToolSnapshot, + type ManagedEnvironment, + type CatalogEnvironmentRecipe, + type ManagedTool, + type ManagedToolVersion, +} from '@/services/environments'; +import { cn } from '@/lib/utils'; + +const ACTIVE_REVISION_STATUSES = new Set(['QUEUED', 'BUILDING', 'SCANNING', 'VERIFYING']); +const RUNTIME_IMAGE_LIMIT_BYTES = 2048 * 1024 * 1024; + +interface EnvironmentForm { + environmentId: string; + name: string; + description: string; + baseEnvironmentId: string; + toolVersionIds: string[]; + aptPackages: string; + environmentVariables: string; + buildCommands: string; +} + +const emptyForm = (): EnvironmentForm => ({ + environmentId: '', + name: '', + description: '', + baseEnvironmentId: 'standard', + toolVersionIds: [], + aptPackages: '', + environmentVariables: '', + buildCommands: '', +}); + +const isCatalogRecipe = ( + recipe: EnvironmentRevision['recipe'] | undefined, +): recipe is CatalogEnvironmentRecipe => recipe?.schemaVersion === 2; + +const resolvedTools = (revision: EnvironmentRevision | null): EnvironmentToolSnapshot[] => { + const recipe = revision?.flattenedRecipe; + if (!isCatalogRecipe(recipe)) return []; + return recipe.resolvedTools ?? recipe.tools; +}; + +const directToolVersionIds = (revision: EnvironmentRevision | null) => + isCatalogRecipe(revision?.recipe) ? revision.recipe.toolVersionIds : []; + +const protectedRuntimeVersions = (revision: EnvironmentRevision | null) => { + const recipe = revision?.flattenedRecipe; + if (!recipe || recipe.schemaVersion !== 1) return { node: null, python: null }; + return { + node: recipe.tools.node?.version ?? null, + python: recipe.tools.python?.version ?? null, + }; +}; + +const formFromRevision = ( + environment: ManagedEnvironment, + revision: EnvironmentRevision | null, +): EnvironmentForm => { + const recipe = revision?.recipe; + return { + environmentId: environment.environmentId, + name: environment.name, + description: environment.description ?? '', + baseEnvironmentId: + environment.environmentId === 'standard' + ? '' + : (recipe?.base?.environmentId ?? environment.baseEnvironmentId ?? 'standard'), + toolVersionIds: directToolVersionIds(revision), + aptPackages: (recipe?.aptPackages ?? []).map((pkg) => `${pkg.name}=${pkg.version}`).join('\n'), + environmentVariables: Object.entries(recipe?.environmentVariables ?? {}) + .map(([name, value]) => `${name}=${value}`) + .join('\n'), + buildCommands: (recipe?.buildCommands ?? []).join('\n'), + }; +}; + +const parsePairs = (value: string) => + value + .split('\n') + .map((line) => line.trim()) + .filter(Boolean) + .map((line) => { + const separator = line.indexOf('='); + return separator > 0 + ? [line.slice(0, separator).trim(), line.slice(separator + 1).trim()] + : [line, '']; + }); + +const recipeFromForm = (form: EnvironmentForm): EnvironmentRecipeInput => ({ + schemaVersion: 2, + toolVersionIds: form.toolVersionIds, + aptPackages: parsePairs(form.aptPackages).map(([name, version]) => ({ name, version })), + environmentVariables: Object.fromEntries(parsePairs(form.environmentVariables)), + buildCommands: form.buildCommands + .split('\n') + .map((line) => line.trim()) + .filter(Boolean), +}); + +const statusClass = (status: string) => { + if (status === 'FAILED') return 'border-destructive/30 bg-destructive/10 text-destructive'; + if (status === 'PUBLISHED' || status === 'READY') + return 'border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300'; + if (status === 'SECURITY_REVIEW' || status === 'UPDATE_AVAILABLE') + return 'border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300'; + if (ACTIVE_REVISION_STATUSES.has(status)) + return 'border-blue-500/30 bg-blue-500/10 text-blue-700 dark:text-blue-300'; + return 'bg-muted/50 text-muted-foreground'; +}; + +const severityClass = (severity: string) => { + if (severity === 'CRITICAL') return 'border-destructive/40 bg-destructive/10 text-destructive'; + if (severity === 'HIGH') + return 'border-orange-500/40 bg-orange-500/10 text-orange-700 dark:text-orange-300'; + if (severity === 'MEDIUM') + return 'border-amber-500/40 bg-amber-500/10 text-amber-700 dark:text-amber-300'; + return 'bg-muted/50 text-muted-foreground'; +}; + +function StatusBadge({ status }: { status: string }) { + return ( + + {status.replaceAll('_', ' ')} + + ); +} + +const publishedVersions = (tool: ManagedTool) => + tool.versions.filter((version) => version.status === 'PUBLISHED'); + +const recommendedVersion = (tool: ManagedTool) => + publishedVersions(tool).find((version) => version.versionId === tool.recommendedVersionId) ?? + publishedVersions(tool)[0] ?? + null; + +function RecipeEditor({ + form, + onChange, + baseOptions, + baseEnvironment, + baseRevision, + baseLoading, + tools, + disabled, + showId, +}: { + form: EnvironmentForm; + onChange: (next: EnvironmentForm) => void; + baseOptions: ManagedEnvironment[]; + baseEnvironment: ManagedEnvironment | null; + baseRevision: EnvironmentRevision | null; + baseLoading: boolean; + tools: ManagedTool[]; + disabled: boolean; + showId: boolean; +}) { + const inherited = resolvedTools(baseRevision); + const protectedVersions = protectedRuntimeVersions(baseRevision); + const inheritedById = new Map(inherited.map((tool) => [tool.toolId, tool])); + const selectedVersions = new Map(); + for (const tool of tools) { + const selected = tool.versions.find((version) => + form.toolVersionIds.includes(version.versionId), + ); + if (selected) selectedVersions.set(tool.toolId, selected); + } + const toolById = new Map(tools.map((tool) => [tool.toolId, tool])); + const effectiveSelectedVersions = new Map(selectedVersions); + const requiredBy = new Map>(); + const resolving = new Set(); + const includeDependencies = (version: ManagedToolVersion) => { + if (resolving.has(version.toolId)) return; + resolving.add(version.toolId); + for (const dependencyId of version.definition.dependencies) { + if (inheritedById.has(dependencyId)) continue; + const owners = requiredBy.get(dependencyId) ?? new Set(); + owners.add(toolById.get(version.toolId)?.name ?? version.toolId); + requiredBy.set(dependencyId, owners); + let dependency = effectiveSelectedVersions.get(dependencyId); + if (!dependency) { + const family = toolById.get(dependencyId); + dependency = family ? (recommendedVersion(family) ?? undefined) : undefined; + if (dependency) effectiveSelectedVersions.set(dependencyId, dependency); + } + if (dependency) includeDependencies(dependency); + } + resolving.delete(version.toolId); + }; + for (const version of selectedVersions.values()) includeDependencies(version); + + const selectedSize = [...effectiveSelectedVersions.values()].reduce( + (total, version) => total + Number(version.imageSizeBytes ?? 0), + 0, + ); + const sizesKnown = + Number(baseRevision?.imageSizeBytes ?? 0) > 0 && + [...effectiveSelectedVersions.values()].every( + (version) => Number(version.imageSizeBytes ?? 0) > 0, + ); + const projectedSize = sizesKnown + ? Number(baseRevision?.imageSizeBytes ?? 0) + selectedSize + : null; + + const setVersion = (tool: ManagedTool, versionId: string | null) => { + const familyVersionIds = new Set(tool.versions.map((version) => version.versionId)); + const remaining = form.toolVersionIds.filter((id) => !familyVersionIds.has(id)); + onChange({ + ...form, + toolVersionIds: versionId ? [...remaining, versionId] : remaining, + }); + }; + + return ( +
+
+ {showId && ( +
+ + onChange({ ...form, environmentId: event.target.value })} + placeholder="generated-from-name" + disabled={disabled} + className="h-9 font-mono text-sm" + /> +
+ )} +
+ + onChange({ ...form, name: event.target.value })} + disabled={disabled} + className="h-9 text-sm" + /> +
+
+ + onChange({ ...form, description: event.target.value })} + disabled={disabled} + className="h-9 text-sm" + /> +
+
+ +
+ + + {baseLoading ? ( + + ) : baseRevision ? ( +
+

+ Inherits protected Node.js and Python plus tools in{' '} + + {baseEnvironment?.name ?? 'the base'} + + . +

+
+ + Node.js{protectedVersions.node ? ` ${protectedVersions.node}` : ''} + + + Python{protectedVersions.python ? ` ${protectedVersions.python}` : ''} + + {inherited.map((tool) => ( + + {tool.name} {tool.version} + + ))} +
+
+ ) : ( +

Published base revision unavailable

+ )} +
+ +
+
+

Catalog tools

+ {projectedSize ? ( + RUNTIME_IMAGE_LIMIT_BYTES && statusClass('FAILED'), + )} + > + Projected {(projectedSize / 1024 / 1024).toFixed(0)} / 2048 MiB + + ) : ( + + Size available after artifacts are built + + )} +
+
+ {tools.map((tool) => { + const versions = publishedVersions(tool); + const inheritedTool = inheritedById.get(tool.toolId) ?? null; + const selected = selectedVersions.get(tool.toolId) ?? null; + const effective = effectiveSelectedVersions.get(tool.toolId) ?? null; + const required = requiredBy.has(tool.toolId) && !inheritedTool; + const enabled = Boolean(effective); + const recommended = recommendedVersion(tool); + const showVersionSelect = + Boolean(inheritedTool && versions.length) || + Boolean(effective && versions.length > 1); + return ( +
+
+
+ {tool.name} + + {effective?.definition.version ?? + inheritedTool?.version ?? + recommended?.definition.version ?? + 'Unavailable'} + + {effective?.versionId === tool.recommendedVersionId && ( + + Recommended + + )} +
+

+ {selected + ? `Added by this environment · ${selected.source?.trustLevel === 'PUBLISHER_VERIFIED' ? 'publisher verified' : 'platform pinned'}` + : required + ? `Added automatically for ${[...(requiredBy.get(tool.toolId) ?? [])].join(', ')}` + : inheritedTool + ? `Inherited from ${baseEnvironment?.name ?? 'base environment'}` + : versions.length + ? tool.description + : 'No published version'} +

+ {(effective?.definition.dependencies.length ?? 0) > 0 && ( +

+ Requires {effective?.definition.dependencies.join(', ')}; missing dependencies + are added at their recommended version. +

+ )} +
+
+ {showVersionSelect && ( + + )} + {!inheritedTool && !required && ( + + setVersion(tool, checked ? (recommended?.versionId ?? null) : null) + } + /> + )} + {required && ( + + Required + + )} + {inheritedTool && !showVersionSelect && ( + + Included + + )} +
+
+ ); + })} + {tools.length === 0 && ( +
+ Publish a tool version before composing an environment. +
+ )} +
+ {projectedSize && projectedSize > RUNTIME_IMAGE_LIMIT_BYTES && ( +

+ + This composition exceeds the AgentCore runtime image limit. +

+ )} +
+ +
+
+ +