Problem
When using @aws-amplify/hosting with self-hosted deployments (ampx deploy --pipeline or standalone ampx deploy), there is no way to reference secrets (API keys, connection ARNs, domain names, etc.) without hardcoding them in source code.
Current state of secrets in Amplify Gen2
| Deployment Mode |
Secret mechanism |
How secrets are set |
| Amplify Managed Hosting |
secret() from @aws-amplify/backend |
Amplify Console UI |
| Cloud Sandbox |
secret() from @aws-amplify/backend |
npx ampx sandbox secret set <key> |
Standalone (ampx deploy) |
❌ None |
Must manually create SSM params at undocumented paths |
Custom Pipeline (definePipeline) |
❌ None |
No mechanism exists |
The existing secret() from @aws-amplify/backend:
- Resolves from SSM at paths like
/amplify/shared/<app-id>/<key> or /amplify/<app-id>/<branch>-branch-<hash>/<key>
- Is tightly coupled to the Amplify managed service (app-id, branch model)
- Cannot be used in
hosting.ts or pipeline.ts
- Has no CLI support for standalone/pipeline deployments
The gap
Users of defineHosting() and definePipeline() have no way to:
- Reference secrets in
hosting.ts (domains, API keys for Lambda env vars, third-party credentials)
- Reference secrets in
pipeline.ts (CodeConnection ARNs, deployment config)
- Set secrets via CLI for standalone/pipeline mode
Proposed Solution
New secret() function in @aws-amplify/hosting
// amplify/hosting.ts
import { defineHosting, secret } from '@aws-amplify/hosting';
defineHosting({
framework: 'nextjs',
domain: secret('DOMAIN_PROD'),
compute: {
environment: {
STRIPE_KEY: secret('STRIPE_KEY'),
},
},
});
// amplify/pipeline.ts
import { definePipeline, secret } from '@aws-amplify/hosting/pipeline';
definePipeline({
source: {
repo: 'my-org/my-app',
connectionArn: secret('CONNECTION_ARN'),
},
branches: [{
branch: 'main',
stages: [
{ name: 'beta', config: { domain: secret('DOMAIN_BETA') } },
{ name: 'prod', config: { domain: secret('DOMAIN_PROD') } },
],
}],
});
CLI for setting secrets
ampx secret set DOMAIN_BETA "beta.example.com"
ampx secret set DOMAIN_PROD "prod.example.com"
ampx secret set CONNECTION_ARN "arn:aws:codeconnections:..."
ampx secret set STRIPE_KEY "sk_live_..."
Design Principles
-
Flat namespace — No magic stage-scoping. secret('DOMAIN_BETA') always resolves to the same SSM parameter regardless of which stage is deploying. Customer picks the key name explicitly.
-
No confusion with Gen2 secret() — This is a SEPARATE function exported from @aws-amplify/hosting, not @aws-amplify/backend. Different deployment model (self-hosted vs managed), different package, different SSM path convention.
-
Works in both hosting.ts and pipeline.ts — Same function, re-exported from @aws-amplify/hosting/pipeline.
-
Explicit and debuggable — No implicit scoping, no hidden behavior. The key you set is the key you reference. 1:1 mapping.
-
SSM-backed — Stored in AWS Systems Manager Parameter Store at a simple, documented path (e.g., /amplify/hosting/<identifier>/<key>).
Technical Considerations
Resolution timing differs by context
| Context |
When secret resolves |
Mechanism |
hosting.ts (Phase 1, CDK stage) |
CloudFormation deploy time |
SSM dynamic reference |
hosting.ts (Phase 2, CodeBuild) |
CodeBuild runtime |
BuildEnvironmentVariableType.PARAMETER_STORE |
pipeline.ts (source config) |
CloudFormation deploy time |
SSM dynamic reference (source action supports it) |
pipeline.ts (stage config) |
Synth time OR CodeBuild runtime |
SDK call or PARAMETER_STORE env var |
Implementation approach
secret('KEY') returns a marker object (not a raw string)
definePipeline() / defineHosting() processes markers:
- For CDK construct props: converts to SSM dynamic reference (CFN resolves at deploy time)
- For Phase 2 CodeBuild env vars: uses
BuildEnvironmentVariableType.PARAMETER_STORE
ampx secret set creates SSM SecureString at the conventional path
- Path convention:
/amplify/hosting/<stack-identifier>/<key> (or simpler: /amplify/self-hosted/<key>)
Why not reuse secret() from @aws-amplify/backend?
- Wrong package —
@aws-amplify/backend is for backend resources (auth, data, storage). Hosting is separate.
- Wrong path convention — Backend secrets use
/amplify/<app-id>/<branch>-branch-<hash>/<key> which requires an Amplify app-id (doesn't exist in self-hosted mode).
- Wrong resolution — Backend
secret() returns BackendSecret type designed for backend construct integration, not hosting/pipeline props.
- Confusion — Having two different
secret() functions with same name but different behavior from different packages would be worse than having clearly named, separate mechanisms.
Acceptance Criteria
Problem
When using
@aws-amplify/hostingwith self-hosted deployments (ampx deploy --pipelineor standaloneampx deploy), there is no way to reference secrets (API keys, connection ARNs, domain names, etc.) without hardcoding them in source code.Current state of secrets in Amplify Gen2
secret()from@aws-amplify/backendsecret()from@aws-amplify/backendnpx ampx sandbox secret set <key>ampx deploy)definePipeline)The existing
secret()from@aws-amplify/backend:/amplify/shared/<app-id>/<key>or/amplify/<app-id>/<branch>-branch-<hash>/<key>hosting.tsorpipeline.tsThe gap
Users of
defineHosting()anddefinePipeline()have no way to:hosting.ts(domains, API keys for Lambda env vars, third-party credentials)pipeline.ts(CodeConnection ARNs, deployment config)Proposed Solution
New
secret()function in@aws-amplify/hostingCLI for setting secrets
Design Principles
Flat namespace — No magic stage-scoping.
secret('DOMAIN_BETA')always resolves to the same SSM parameter regardless of which stage is deploying. Customer picks the key name explicitly.No confusion with Gen2
secret()— This is a SEPARATE function exported from@aws-amplify/hosting, not@aws-amplify/backend. Different deployment model (self-hosted vs managed), different package, different SSM path convention.Works in both
hosting.tsandpipeline.ts— Same function, re-exported from@aws-amplify/hosting/pipeline.Explicit and debuggable — No implicit scoping, no hidden behavior. The key you set is the key you reference. 1:1 mapping.
SSM-backed — Stored in AWS Systems Manager Parameter Store at a simple, documented path (e.g.,
/amplify/hosting/<identifier>/<key>).Technical Considerations
Resolution timing differs by context
hosting.ts(Phase 1, CDK stage)hosting.ts(Phase 2, CodeBuild)BuildEnvironmentVariableType.PARAMETER_STOREpipeline.ts(source config)pipeline.ts(stage config)Implementation approach
secret('KEY')returns a marker object (not a raw string)definePipeline()/defineHosting()processes markers:BuildEnvironmentVariableType.PARAMETER_STOREampx secret setcreates SSM SecureString at the conventional path/amplify/hosting/<stack-identifier>/<key>(or simpler:/amplify/self-hosted/<key>)Why not reuse
secret()from@aws-amplify/backend?@aws-amplify/backendis for backend resources (auth, data, storage). Hosting is separate./amplify/<app-id>/<branch>-branch-<hash>/<key>which requires an Amplify app-id (doesn't exist in self-hosted mode).secret()returnsBackendSecrettype designed for backend construct integration, not hosting/pipeline props.secret()functions with same name but different behavior from different packages would be worse than having clearly named, separate mechanisms.Acceptance Criteria
secret()function exported from@aws-amplify/hostingsecret()re-exported from@aws-amplify/hosting/pipelineampx secret set <key> <value>CLI command for self-hosted modeampx secret listCLI commandampx secret remove <key>CLI commanddefineHosting()props (domain, compute.environment)definePipeline()props (connectionArn, stage config)