SSM (Parameter Store)

Lightweight, dependency-free fake of AWS Systems Manager (SSM) Parameter Store that speaks the real AWS JSON 1.1 wire protocol, so application code using @aws-sdk/client-ssm can run against it with zero cost and zero side effects.

KeyValue
Port4578
ProtocolAWS JSON 1.1 (X-Amz-Target: AmazonSSM.<Operation>) over HTTP
Compatible client@aws-sdk/client-ssm (v3)
Size~60 KB
Startup< 100ms
StateIn-memory, ephemeral, resettable

Quick Start

Start the server:

import { SsmServer } from "./services/ssm/src/server.js";

const server = new SsmServer(4578);
await server.start();
// ... use it ...
await server.stop();

Connect with the real AWS SDK client:

import {
  SSMClient,
  PutParameterCommand,
  GetParameterCommand,
  GetParametersByPathCommand,
} from "@aws-sdk/client-ssm";

const ssm = new SSMClient({
  region: "us-east-1",
  endpoint: "http://127.0.0.1:4578",
  credentials: { accessKeyId: "parlel", secretAccessKey: "parlel" },
});

// Write a parameter
await ssm.send(
  new PutParameterCommand({ Name: "/app/db/host", Value: "localhost", Type: "String" }),
);

// Write a secret
await ssm.send(
  new PutParameterCommand({ Name: "/app/db/password", Value: "s3cr3t", Type: "SecureString" }),
);

// Read it back
const { Parameter } = await ssm.send(new GetParameterCommand({ Name: "/app/db/host" }));
console.log(Parameter.Value); // "localhost"

// Fetch a whole hierarchy
const tree = await ssm.send(
  new GetParametersByPathCommand({ Path: "/app", Recursive: true }),
);
console.log(tree.Parameters.map((p) => p.Name));

Configuration via environment variables

The @aws-sdk/client-ssm client honors these, set them to point the SDK at the fake:

AWS_ACCESS_KEY_ID=parlel
AWS_SECRET_ACCESS_KEY=parlel
AWS_REGION=us-east-1
AWS_ENDPOINT_URL_SSM=http://127.0.0.1:4578
AWS_ENDPOINT_URL=http://127.0.0.1:4578

Implemented operations

Parameter CRUD

Version labels

Tagging

Resource policies

Service settings

Parlel internal (not part of SSM)

Surface coverage

This emulator faithfully replicates the API surface most application code and agents exercise. Anything below the supported lines is either an intentional design choice for a fast, zero-cost local emulator (✓ By design) or a candidate for a future release (⟳ Roadmap) — never a silent inaccuracy.

Legend: ✅ fully supported · ◐ accepted (stored, not strictly enforced) · ✓ by design · ⟳ on the roadmap.

FeatureStatus
Parameter Store CRUD (PutParameter, GetParameter(s), GetParametersByPath, Delete..., Describe..., GetParameterHistory)✅ Supported
Parameter versioning + version selectors (name:version)✅ Supported
Version labels + label selectors (name:label)✅ Supported
Parameter types String / StringList / SecureString✅ Supported
Tiers Standard / Advanced / Intelligent-Tiering (size-based promotion)✅ Supported
AllowedPattern validation✅ Supported
Hierarchies / recursive path queries / ParameterFilters✅ Supported
Tagging (AddTagsToResource, RemoveTagsFromResource, ListTagsForResource)✅ Supported
Resource policies (PutResourcePolicy, GetResourcePolicies, DeleteResourcePolicy)✅ Supported
Service settings (Get/Update/Reset ServiceSetting)✅ Supported
Pagination (MaxResults / NextToken)✅ Supported
Real KMS encryption of SecureString✓ By design — Always succeeds deterministically — no real funds move
Policies (expiration / no-change notifications) actually firing⟳ Roadmap — Stored + echoed in metadata, but never executed
Automation, Documents (SSM Docs), Run Command (SendCommand), Sessions⟳ Roadmap — fleet/operations, not Parameter Store
Maintenance Windows, Patch Baselines, Inventory, OpsItems, Associations⟳ Roadmap
Cross-account Shared parameters⟳ Roadmap

Error codes / shapes

Errors are returned with a non-2xx HTTP status and an AWS JSON 1.1 body:

{ "__type": "ParameterNotFound", "message": "Systems Manager could not find the parameter /missing." }

The x-amzn-errortype response header carries the same code. The @aws-sdk/client-ssm client maps __type to error.name.

CodeHTTPWhen
ParameterNotFound400The named parameter (or label) does not exist
ParameterAlreadyExists400PutParameter on an existing name without Overwrite: true
ParameterVersionNotFound400A name:version selector points at a non-existent version
ParameterPatternMismatchException400Value fails AllowedPattern
InvalidAllowedPatternException400AllowedPattern is not a valid regular expression
ValidationException400Missing/invalid input (no Type on create, bad Type, reserved name prefix, oversized value, empty Names, bad Path, etc.)
InvalidResourceId400Tagging a parameter that does not exist
InvalidFilterKey400Unknown filter key for Describe/GetParametersByPath
InvalidNextToken400Malformed pagination token
MalformedResourcePolicyDocumentException400PutResourcePolicy with non-JSON policy
ResourcePolicyNotFoundException400DeleteResourcePolicy for an unknown PolicyId
ResourcePolicyConflictException400PutResourcePolicy update with a stale PolicyHash
InternalFailure500Unexpected server error

Notes on fidelity

<!-- parlel:testenv:start -->

Configuration — test.env

Copy these into your test.env (used by the bridge sidecar flow). Tokens are Parlel's seeded test credentials — any non-empty value is accepted by the emulator, so you rarely need to change them. Swap in real credentials only when pointing at the live service in prod.env.

AWS_ACCESS_KEY_ID=parlel
AWS_SECRET_ACCESS_KEY=parlel
AWS_REGION=us-east-1
AWS_ENDPOINT_URL_SSM=http://parlel-bridge:4578
AWS_ENDPOINT_URL=http://parlel-bridge:4578
<!-- parlel:testenv:end -->