Ashlar is pre-1.0; the first tagged release is v0.1.0 (2026-08-30). Only the most recent release line (0.1.x) and master are supported with security fixes.
Please do not open a public issue for a security vulnerability.
- Preferred: report privately via GitHub Security Advisories.
- Alternatively: contact the maintainer, @IanFrelinger.
Include the affected component (project or path), a reproduction or proof of concept, and your assessment of the impact. You should receive an acknowledgement within 7 days.
Ashlar's security-relevant surface includes the trust path components: policy gates, sanitization and PII/secret filters, audit trails and barrier identity, mesh/federation transport, and execution-target routing (local, cloud, peer). Reports about weaknesses in these boundaries are especially welcome, as are reports about the standard surfaces (Ashlar.API, Ashlar.CLI, container images, compose deployments).
What a fresh checkout does when you run it, and what you must turn on before it faces a network.
What listens where by default
Ashlar.API(dotnet run,.docker/Dockerfile.api,docker-compose.portal.yml) serves HTTP only.ASPNETCORE_URLSdefaults to loopback fordotnet run; every shipped compose file publishes on127.0.0.1.AllowedHostsis*in the sharedappsettings.json(mesh peers and tailnet names must resolve);appsettings.Development.jsonnarrows it tolocalhost;127.0.0.1;[::1].Ashlar:Security:ExposureProfileisLocalhostandAshlar:Security:AuthorizationModeisNone: read and mutating routes under/apianswer without credentials on loopback. That is the intended local-development posture, not a deployment posture.- Nothing under
/apicalls a container runtime or accepts host paths by default: the remote execution surface (below) is unmapped, and the MCP / A2A protocol surfaces are feature-flagged off.
What needs auth, and how to enable it
- Set
Ashlar:Security:AuthorizationModetoApiKey,BearerToken,Basic, or one of the OR-composites, plus the matching credential (ApiKey,BearerToken,BasicAuthUsername/BasicAuthPassword).AuthorizationScope=MutatingApi(default) credentials POST/PUT/PATCH/DELETE under/apiand every verb on/api/mcpand/api/a2a;AllApicredentials GETs too. One family of reads is credentialed under either scope:GET /api/copilot/tasks*(task history carries the prompts and outputs of past runs), controlled byAshlar:Security:RequireAuthForCopilotReadApis, which defaults totrue. Credentials are compared in constant time against the configured plaintext value (they are not hashed at rest), so keep them in environment or secret stores, not committed JSON. Details:docs/Configuration.md, "Ashlar.API exposure". - The legacy
RequireApiKeyForMutatingEndpoints=trueflag fails closed: with noApiKeyconfigured every mutating request is rejected (401) rather than passed through. - Tenant / org / user identity on the multi-tenant surfaces (
X-Ashlar-Tenant,X-Ashlar-User,X-Ashlar-Org) is client-asserted. Trust those headers only behind built-in auth or an authenticating proxy that sets them;docker-compose.cloud-multi-tenant.ymltherefore requiresASHLAR_API_KEY.
Exposure fails closed
ExposureProfileofLan,TailnetorPublicwithAuthorizationMode=None(and the legacy flag off) makesAshlar.APIrefuse to start, with a message that names the fix. The profile still does not configure firewalls, Tailscale ACLs or TLS; do that separately.- Escape hatch:
Ashlar:Security:AllowUnauthenticatedNetworkExposure=trueturns the refusal into a startup warning. Set it only when an authenticating reverse proxy or a network ACL is the whole auth story (scripts/start-ashlar-api-dev.*set it for--listen-lanand say so loudly).
Remote container execution is opt-in
POST /api/execution/buildandPOST /api/execution/runhand caller-chosen images, commands and host bind mounts to this host's Docker daemon on behalf of aRemoteExecutionPlatformclient (ASHLAR_EXECUTION_REMOTE_URL). They are mapped only whenAshlar:Execution:ServeRemoteExecution=true(404 otherwise), refuseAuthorizationMode=Nonewith 403 even when opted in, and reject anyVolumeMounts(400) unlessAshlar:Execution:AllowedVolumeMountRootnames the single host directory they may live under.- Shipped compose files never mount
docker.sockintoAshlar.API; the in-processDockerExecutionPlatformused byashlar test --platformon the CLI is unaffected by the opt-in.
Commercial hosts
Ashlar.Commercial.Fleet.HostrunsAuthorizationMode=ApiKeyand ships no key: supplyAshlar__Security__ApiKey(compose:ASHLAR_API_KEY, required with no default) or every mutating request is rejected.