GovCloud reference deployment
NativeCAD in your own AWS GovCloud (US) account, inside a VPC that has no route to the internet. This guide is for the admin who stands it up. It builds on the air-gap deployment guide: the kernel and the MCP server are pure JavaScript with no native dependencies, and nothing in them phones home. This page adds one container image, one CloudFormation stack, and the steps that connect the two.
What you get:
- One image (
deploy/container/Dockerfile). It holds the NativeCAD MCP server, which an agent drives over stdio, and the browser modeler, which people use to edit parts. It runs as UID 1001 with a read-only root filesystem./workspaceis the only writable path. - One stack (
deploy/govcloud/nativecad-govcloud.template.json). It creates a private-only VPC, VPC endpoints for ECR, S3, CloudWatch Logs and Bedrock runtime (FIPS), an ECS Fargate service behind an internal load balancer, a KMS-encrypted EFS volume for/workspace, KMS-encrypted logs, VPC flow logs and a CloudTrail trail. The stack defaults tous-gov-west-1and refuses to create in any other region than the one you name. - Sandbox mode, on by default. The MCP server in the image can only read and write inside
/workspace, cannot open a network connection from inside the process, and writes a hash-chained audit line for every tool call. See Sandbox mode below. - A test that proves both (the container acceptance test). It builds the image and runs it with
docker run --network none. In that session it drives a real MCP run (box, fillet, save as STEP) and serves the modeler. It also validates the stack offline against the no-egress contract below.
The no-egress contract
node deploy/govcloud/validate.js checks each of these properties by name. It needs no AWS account and no network, and CI runs it on every push:
| property | how the stack enforces it |
|---|---|
| no route out of the VPC | no internet gateway, NAT gateway, egress-only IGW, Elastic IP, VPN/Transit Gateway attachment or peering. The route table has no routes beyond the implicit local route and the S3 gateway endpoint's prefix-list route. |
| AWS APIs over private endpoints only | VPC endpoints are exactly bedrock-runtime-fips, ecr.api, ecr.dkr, logs (interface, private DNS) and s3 (gateway). The S3 endpoint policy allows only GetObject on the regional ECR layer bucket. |
| nothing public | subnets never assign public IPs, tasks run with AssignPublicIp: DISABLED, and the load balancer scheme is internal |
| encrypted at rest | one customer-managed KMS key with rotation on. It encrypts EFS /workspace, both log groups and the trail bucket (SSE-KMS). EFS also enforces TLS in transit. |
| least privilege | the root filesystem is read-only, the user is non-root, all Linux capabilities are dropped, and ECS Exec is off. The task role can only mount its own EFS access point, because the application makes no AWS API calls. |
| audited | CloudTrail is on, multi-region, with log-file validation and KMS encryption. VPC flow logs capture ALL traffic, which gives your assessor direct evidence that nothing leaves. |
The image adds its own guarantees on top. Telemetry is off, set explicitly by DO_NOT_TRACK=1 and CADSOLVER_TELEMETRY=off. CADSOLVER_MCP_SHARE_OFFLINE=1 makes a confirmed share write its bundle locally instead of opening a socket. The modeler is served with a Content-Security-Policy of connect-src 'self', so the page in the user's browser cannot call out either.
Sandbox mode
The image sets NATIVECAD_SANDBOX=1, so the MCP server starts in sandbox mode. The VPC above stops traffic at the network. Sandbox mode stops it in the process, and records what the agent did. It is built for ITAR and CUI data, and it does three things.
1. The workspace jail. Every file path a tool is given is checked before the tool runs. That covers cad_import, cad_save, cad_export_body, drawing, print and slice outputs, FEA case directories, cad_configure's outputDirectory, and paths nested inside cad_batch. The path is resolved first: relative paths against the output directory and the working directory, ~ against home, and every symlink on the way, dangling ones included. A path that lands outside NATIVECAD_WORKSPACE (/workspace here) is refused with reason code SANDBOX_PATH, and the file is never opened. The audit directory itself is also off limits to tools.
2. No egress, from inside the process. The server replaces Node's network entry points before any module loads: http, https, net, tls, dgram, dns, fetch and WebSocket. Spawning a network tool (curl, wget, ssh, git, npm and the like) is refused too. Any attempt throws with reason code SANDBOX_EGRESS. Worker threads and child Node processes inherit the same guard. Sandbox mode also forces these switches, whatever the environment says:
| switch | forced to | what it stops |
|---|---|---|
NATIVECAD_AUTO_UPDATE | off | update checks and revocation-list downloads |
CADSOLVER_TELEMETRY, DO_NOT_TRACK | off, 1 | usage telemetry and corpus capture |
CADSOLVER_MCP_SHARE_OFFLINE | 1 | eval sharing (a local bundle only) |
CADSOLVER_REVOKED_FILE | NATIVECAD_REVOCATION_FILE, else /workspace/revoked.json | licence revocation is read from a file you place, never fetched |
cad_configure cannot switch telemetry, capture or auto-update back on. Tools that only make sense with a network or an outside toolchain are refused with SANDBOX_UNAVAILABLE: cad_report_bug, cad_quote_submit, cad_eval_share, cad_contest, cad_print, cad_print_status, cad_picogk, cad_rtl and cad_synth. cad_server_info lists each one with its reason under sandbox.unavailable. It also counts refused egress attempts and names the audit file.
3. The audit log. Every tool call, refusals included, is appended to /workspace/audit/audit-<UTC time>-<pid>-<id>.jsonl. Each server process gets its own file, so two tasks sharing the EFS volume never interleave. One line holds:
- the ISO time, the tool, and the arguments with file paths (geometry payloads are replaced by a length and a SHA-256)
- the SHA-256 of every file read or written
- the result status, error code and
reasonCode - the document handle, the client scope and the actor (
agent, orhumanwhen the call's_meta["nativecad/actor"]orNATIVECAD_ACTORsays so)
Each line carries the hash of the line before it. Verify a log with:
node tools/audit_verify.js /workspace/audit # exit 0 = every chain intact
node tools/audit_verify.js /workspace/audit --require-sealed # also fail an unsealed file
If a line is edited, inserted, removed or moved, verification fails and names the first broken line. A cleanly stopped server seals its file with a session-end line. A hash chain cannot prove that a whole file was not deleted, so ship /workspace/audit/ to WORM storage (CloudWatch Logs, or S3 with Object Lock) on a schedule. If the server cannot write an audit line, it refuses every later call with SANDBOX_AUDIT rather than run unaudited. The mcp-mode health probe writes to /workspace/audit/healthcheck/, so probes do not bury session logs. NATIVECAD_AUDIT_DIR moves the log, but only within the workspace.
To turn sandbox mode off for a non-CUI deployment, use -e NATIVECAD_SANDBOX=0. To turn it on anywhere else, use nativecad-mcp --sandbox --workspace <dir>, or set NATIVECAD_SANDBOX=1 and NATIVECAD_WORKSPACE. A server asked for sandbox mode with no workspace refuses to start.
Steps for your admin
Steps 1 and 2 run on a connected build host. Everything after that runs inside your boundary.
- Build the image. Run this from the repo root:
docker build -f deploy/container/Dockerfile -t nativecad:govcloud .
The base is Red Hat UBI 9 nodejs-22-minimal, pinned by digest. UBI is freely redistributable and FIPS-capable: RHEL 9's OpenSSL is the FIPS 140-3 validated module, so on a FIPS-mode host crypto.getFips() reports 1 with no image change. To use an Iron Bank Node image instead, pass --build-arg BASE_IMAGE=registry1.dso.mil/ironbank/opensource/nodejs/nodejs22:<tag>@sha256:<digest>.
If the build host sits behind a TLS-inspecting proxy or uses an internal npm mirror, add any of these: --build-arg HTTPS_PROXY=…, --build-arg NPM_CONFIG_REGISTRY=https://nexus.example/npm/, --secret id=build_ca,src=corp-ca.pem. The CA is mounted for one build step and is never written into a layer.
- Prove it offline before it crosses. Run the container acceptance test. It runs the network-off session and the offline stack validation. Then scan the image with your own tooling and record its digest with
docker inspect --format '{{index .RepoDigests 0}}'. - Push it to a private ECR repository in your GovCloud account. Use your usual transfer path, for example
docker save→ media →docker load→docker push. The stack only pulls from ECR, through the VPC endpoint. - Create the stack.
aws cloudformation deploy --region us-gov-west-1 \
--stack-name nativecad \
--template-file deploy/govcloud/nativecad-govcloud.template.json \
--capabilities CAPABILITY_IAM \
--parameter-overrides \
ImageUri=<acct>.dkr.ecr.us-gov-west-1.amazonaws.com/nativecad@sha256:<digest> \
ClientCidr=<your enclave/VDI range> \
CertificateArn=<ACM cert ARN for the internal ALB> \
S3PrefixListId=<pl-… from: aws ec2 describe-managed-prefix-lists --filters Name=prefix-list-name,Values=com.amazonaws.us-gov-west-1.s3>
Two parameters you can set to fit your account. CreateCloudTrail=false skips the trail when your account already has an organization trail. ExpectedRegion=us-gov-east-1 targets the other GovCloud region.
- Connect your network to the VPC through your own Transit Gateway, Direct Connect or VPN attachment, so users in
ClientCidrcan reach the load balancer. The stack deliberately creates none of these. They belong to your boundary, not to this stack. - Open the modeler at the
ModelerUrlstack output. - Run agent (MCP) sessions. The MCP server speaks stdio and has no port. Pick one of two patterns:
- Run the image on an admin or agent host inside the boundary:
docker run --rm -i --network none --read-only --tmpfs /workspace:uid=1001 -e CADSOLVER_LICENSE_KEY=… <image> mcp. The MCP host wiring is the same as in the air-gap deployment guide. - Run a one-off Fargate task from the
TaskDefinitionArnoutput with the command overridden to["mcp"]. Batch jobs that pipe a session script into stdin fit this pattern. - Licence: set
CADSOLVER_LICENSE_KEY, either as an ECS task environment value or from your secrets store. It is validated locally, with no licence server. See the licensing section of the air-gap deployment guide.
Updates: build a new image, run step 2 again, push it, then redeploy the stack with the new ImageUri digest. The service's deployment circuit breaker rolls back automatically if the new task fails its health check.
Model inside the boundary
A sandboxed CAD server is half the boundary. For CUI work the agent's model calls must stay in GovCloud too. The research behind this section, with dates and citations, is in our model-boundary research notes (checked 2026-10-11). Short version:
- Models. Amazon Bedrock in GovCloud lists Claude Opus 4.8, Sonnet 5, Sonnet 5.5, Opus 5.5 and Haiku 5.5. On
bedrock-runtimethey are reachable only through the GovCloud geographic inference profile (us-gov.prefix), in us-gov-west-1 and us-gov-east-1. The native Messages endpoint (bedrock-mantle) serves Sonnet 5.5 and Haiku 5.5 in-region in us-gov-west-1 only. Commercialus.andglobal.profiles, and any model not in that list, are not available in GovCloud and must not be configured. - Authorisation. AWS announced FedRAMP High and DoD IL4/IL5 for Bedrock in GovCloud on 2025-05-23, naming Claude 3.5 Sonnet v1 and Claude 3 Haiku. We could not confirm a per-model statement for the 2026 models from a primary source. Confirm with AWS (Services in Scope, AWS Artifact) that your chosen model ID is inside your authorised scope. This stack does not make that claim for you.
- EULA. Opus 4.8 needs the EULA accepted in the linked commercial account; it cannot be accepted in GovCloud. Plan that administrative step before the first call.
- Private path. The stack creates the
bedrock-runtime-fipsinterface endpoint (private DNSbedrock-runtime-fips.<region>.amazonaws.com), with a policy that allows onlyInvokeModelandInvokeModelWithResponseStreamon Anthropic models. AWS documents the FIPS Bedrock endpoint services in both GovCloud regions. The non-FIPSbedrock-runtimeandbedrock-mantleservices are not documented for GovCloud, so the stack does not add them. Check your account withaws ec2 describe-vpc-endpoint-services --region us-gov-west-1 --service-names com.amazonaws.us-gov-west-1.bedrock-runtime.
Agent host configuration (Claude Code or an Agent SDK host on an instance or task inside the VPC). Pin every model: unpinned, Claude Code defaults to Opus 5.5 and a Sonnet 4.5 background model, and Sonnet 4.5 is not in GovCloud.
export CLAUDE_CODE_USE_BEDROCK=1
export AWS_REGION=us-gov-west-1
export AWS_USE_FIPS_ENDPOINT=true
export ANTHROPIC_BEDROCK_BASE_URL=https://bedrock-runtime-fips.us-gov-west-1.amazonaws.com
export ANTHROPIC_MODEL=us-gov.anthropic.claude-sonnet-5-5
export ANTHROPIC_DEFAULT_SONNET_MODEL=us-gov.anthropic.claude-sonnet-5-5
export ANTHROPIC_DEFAULT_OPUS_MODEL=us-gov.anthropic.claude-opus-5-5
export ANTHROPIC_DEFAULT_HAIKU_MODEL=us-gov.anthropic.claude-sonnet-5-5 # keep background tasks on a model that exists in GovCloud
export DISABLE_TELEMETRY=1 DISABLE_ERROR_REPORTING=1 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 DISABLE_AUTOUPDATER=1
Verify the IDs your account can call before rollout: aws bedrock list-inference-profiles --region us-gov-west-1, then /status in Claude Code to confirm the provider and region.
- Credentials: use the instance profile or ECS task role. Avoid
awsAuthRefreshand SSO flows, which call STS, and the stack has nostsendpoint. A Bedrock API key (AWS_BEARER_TOKEN_BEDROCK) also skips the credential chain. - IAM: allow
bedrock:InvokeModel,bedrock:InvokeModelWithResponseStream,bedrock:ListInferenceProfilesandbedrock:GetInferenceProfileon the inference-profile and foundation-model ARNs. Use the GovCloud partition,arn:aws-us-gov:bedrock:.... Thearn:aws:ARNs in the Claude Code docs are commercial. - Not available on Bedrock: Claude Code's WebSearch tool. Claude Code uses the Invoke API, not Converse.
- Fallback: if
ANTHROPIC_BEDROCK_BASE_URLis not honoured by your agent host's SDK, keep private DNS on the endpoint and set onlyAWS_USE_FIPS_ENDPOINT=true; confirm in VPC flow logs that calls hit the endpoint network interfaces and nothing else. - Cross-region note: a
us-gov.profile may serve a request from either GovCloud region. Data stays in the GovCloud partition, not necessarily in one region.
What your ATO / boundary owns
The stack is a reference. It is designed to drop into an accredited boundary, and it does not replace that accreditation. Here is the split. For a requirement-by-requirement version against NIST SP 800-171 and CMMC Level 2, see the CMMC responsibility map. A CycloneDX SBOM for the bundle is in deploy/sbom/.
| NativeCAD provides | your ATO / boundary owns |
|---|---|
the image, its SBOM-able dependency closure (3 npm packages plus the staged kernel; see kernel/BUILD_INFO.json), and the Dockerfile that builds it | the image scan, approval and the registry it lives in |
| a stack that has no egress path and validates offline | the AWS account, its SCPs, IAM boundaries and the GovCloud account itself |
an internal load balancer with an optional FIPS TLS policy (ELBSecurityPolicy-TLS13-1-2-FIPS-2023-04) | the ACM certificate, DNS and the PKI behind it; user authentication in front of the modeler (your IdP, VDI or reverse proxy) |
| a VPC with no route out | the connectivity INTO it: Transit Gateway, Direct Connect or VPN, and the ClientCidr value |
| a KMS key, encrypted EFS, logs and trail | key administration policy, log retention beyond LogRetentionDays, SIEM forwarding and backup verification |
| CloudTrail and VPC flow logs switched on | continuous monitoring, incident response and the SSP/POA&M entries |
| telemetry and sharing switched off in the image | your policy on what users may export, and the media-transfer process for anything that crosses |
| sandbox mode: a workspace jail, an in-process egress block, and a hash-chained audit log with a verifier | shipping /workspace/audit/ to WORM storage, reviewing it, and placing a current signed revoked.json on each media transfer |
Files
| path | what it is |
|---|---|
deploy/container/Dockerfile | the one image: MCP stdio (mcp, the default) and the browser modeler (web) |
deploy/container/Dockerfile.dockerignore | build-context list. It starts from the air-gap list and keeps only the modeler's files from web/. |
deploy/container/entrypoint.js | mode switch, workspace setup and the healthcheck |
deploy/container/serve-modeler.js | static server for the modeler: GET/HEAD only, /healthz, CSP and a path-traversal guard |
deploy/govcloud/nativecad-govcloud.template.json | the CloudFormation stack |
deploy/sbom/nativecad-mcp-<version>.cdx.json | the CycloneDX SBOM for the staged MCP bundle, written by tools/sbom_cyclonedx.js |
deploy/govcloud/validate.js | the offline validator: structural checks, the no-egress contract, and cfn-lint when it is installed |
| the container acceptance test | the proof: an image build, a network-off MCP session, the modeler, and the stack validation |
packages/mcp/src/sandbox/ | sandbox mode: the jail and gate (index.js), the egress guard (egress.js, preloaded by preload.js) and the audit chain (audit.js) |
tools/audit_verify.js | verifies the audit hash chains |
| the sandbox acceptance test | the proof for sandbox mode: path escapes, egress attempts, inert bug-intake and update paths, and audit verification with tampering |
Why CloudFormation and not Terraform: the repo had no infrastructure code before this. A CloudFormation template validates fully offline with cfn-lint. Terraform's validate needs terraform init to download the AWS provider first, which is awkward on a disconnected admin host. GovCloud supports CloudFormation natively.
Want this running inside your boundary? We will walk your team through the deployment and the map.
Talk to sales: sales@featureboard.ai