Skip to content

Repository files navigation

@aureatus/keystone

TypeScript-first env tooling for repos that want one manifest-driven way to load, validate, generate, and clear environment files.

Keystone now also includes an experimental service-map layer for resolving local-process, Docker-style, and Portless-exposed service endpoints before env files are generated.

It is still marked experimental because the service-map contract has not yet been proven against a real consumer like Hive, and the public schema may still tighten as we validate real orchestration flows.

The package now builds to dist/ with tsup, so npm/file installs consume compiled library and CLI artifacts rather than raw source files.

For consumers outside TypeScript, Keystone should be treated as having two contracts:

  • a TypeScript SDK exported from @aureatus/keystone
  • an OpenAPI wire contract at openapi/service-map.openapi.yaml for service-to-service or cross-runtime consumption

The OpenAPI file is generated from Zod schemas in src/openapi/service-map-contract.ts.

What it provides

  • defineManifest(...) for declaring env sources and generated outputs
  • keystone init for bootstrapping local env files from templates
  • keystone generate for materializing derived .env files
  • keystone doctor for smoke-level validation
  • keystone scan-secrets for checking template hygiene and public-output leaks
  • keystone clear for removing generated outputs
  • shared helpers for env parsing and free-port allocation
  • experimental service map support with bundled Portless-aware public URL generation

CLI

keystone init --manifest env.manifest.ts
keystone generate --manifest env.manifest.ts
keystone doctor --manifest env.manifest.ts
keystone scan-secrets --manifest env.manifest.ts
keystone clear --manifest env.manifest.ts
keystone service-map resolve --manifest env.manifest.ts --json
keystone service-map resolve --manifest env.manifest.ts --context service-map.context.json --json
keystone service-map render --manifest env.manifest.ts --service api
keystone service-map render --manifest env.manifest.ts --service api --format json --output api-env.json
keystone service-map resolve --manifest env.manifest.ts --output service-map.json
keystone service-map resolve --manifest env.manifest.ts --output service-map.pretty.json --pretty

Minimal manifest

import { defineManifest } from "@aureatus/keystone";

export default defineManifest({
  name: "example",
  sourceFiles: [".env", ".env.local"],
  initFiles: [
    {
      templatePath: ".env.example",
      destinationPath: ".env",
    },
  ],
  variables: {
    API_KEY: {
      required: true,
      secret: true,
    },
  },
  outputs: [
    {
      path: ".generated/app.env",
      includeKeys: ["API_URL", "PORT"],
      header: ["# Generated by Keystone"],
    },
  ],
});

Service Map Direction

Keystone's next major layer is service-aware endpoint resolution for local processes, Portless-exposed HTTP services, and Docker or Compose-backed dependencies.

  • design notes: docs/service-map.md
  • goal: keep defaults small and documented, and make every inferred host, port, and URL easy to override
  • planned use case: let orchestrators like Hive resolve per-cell endpoints and generated env in one place

The first implementation slice lives behind experimental.serviceMap in the manifest and supports:

  • local-process, docker-published, and docker-network runtimes
  • direct, portless, and none exposure modes
  • cross-service env bindings without encoding startup order
  • bundled Portless hostname and public-URL formatting for HTTP services

The intended long-term split is:

  • TypeScript repos can use the SDK directly
  • non-TypeScript runtimes such as Hive's Elixir backend can consume the service-map output through the OpenAPI contract

Useful commands:

bun run fixtures:update
bun run openapi:generate
bun run openapi:check

Every resolved service map now includes a schemaVersion field so external consumers like Hive can guard against contract changes.

Docker Compose helper:

import {
  createServiceMapContextFromDockerCompose,
  resolveServiceMap,
} from "@aureatus/keystone";

const context = createServiceMapContextFromDockerCompose(manifest, {
  projectName: "my-stack",
  services: {
    api: {
      serviceName: "api",
      ports: [{ containerPort: 8080, publishedPort: 18080 }],
    },
    postgres: {
      serviceName: "postgres",
      networkHost: "postgres",
      ports: [{ containerPort: 5432, publishedPort: 15432 }],
    },
  },
});

const serviceMap = await resolveServiceMap(manifest, {
  repoRoot: process.cwd(),
  context,
});

For cross-runtime consumers that do not want to call the TypeScript API directly, Keystone also exposes a one-shot CLI resolver:

keystone service-map resolve --manifest env.manifest.ts --json

That command prints a JSON payload shaped like ResolvedServiceMap from the generated OpenAPI contract.

If you want a concrete env projection instead of the full resolved object, use service-map render.

Output options:

  • --context <path> loads a structured JSON context file for cell/runtime-specific service-map inputs
  • --json prints the resolved service map to stdout
  • --output <path> writes the resolved service map to a file
  • --pretty pretty-prints JSON when writing to a file

Render options:

  • --service <name> renders only one service's env projection
  • --format env|json chooses env-file or JSON output; default is env

Example output from the smoke fixture is checked in at fixtures/smoke-workspace/service-map.example.json. An example structured context file is checked in at fixtures/smoke-workspace/service-map.context.example.json. A context-resolved output example is checked in at fixtures/smoke-workspace/service-map.context.resolved.json.

Additional mock-repo fixtures live under fixtures/mock-repos/ and are covered by tests. They include:

  • fixtures/mock-repos/portless-web-api
  • fixtures/mock-repos/docker-compose-stack
  • fixtures/mock-repos/mixed-fullstack

bun run fixtures:update regenerates the checked-in service-map.example.json files for the smoke fixture and all mock repos.

SDK usage:

import {
  renderServiceEnv,
  renderServiceMapEnvFile,
  resolveServiceMap,
} from "@aureatus/keystone";

const serviceMap = await resolveServiceMap(manifest, {
  repoRoot: process.cwd(),
  context: {
    cellName: "auth-fix",
    portless: { rootName: "auth-fix" },
    services: {
      api: { preferredPort: 5512 },
      web: { preferredPort: 5513 },
    },
  },
});

if (!serviceMap) {
  throw new Error("Service map is not enabled for this manifest.");
}

const apiEnv = renderServiceEnv(serviceMap, "api");
const serviceMapEnvFile = renderServiceMapEnvFile(serviceMap, {
  header: ["# Generated by Keystone"],
});

Agent bootstrap

If you want to hand Keystone setup to an AI coding agent in another repo, copy and paste this prompt.

Published package prompt
Set up Keystone env management in this repository.

Goals:
- inspect the existing environment-variable workflow first
- summarize the current env flow before making changes, including source files, loaders, generators, validation, and developer entrypoints
- install `@aureatus/keystone`
- create an `env.manifest.ts` at the repo root
- preserve current developer workflows where possible, but route them through Keystone
- add scripts for `keystone init`, `keystone generate`, `keystone doctor`, `keystone scan-secrets`, and `keystone clear`
- mark secret env vars as `secret: true`
- mark client/browser/public outputs as `public: true`
- update `.gitignore` for generated env files
- update the repo README with the new env workflow
- run verification and report exactly what changed

Constraints:
- do not invent extra abstractions if the repo already has a clear env flow
- keep the migration incremental and compatible first
- prefer one canonical variable name per concept; use aliases only when truly needed
- do not expose secret vars to public outputs

What I want you to deliver:
1. a short audit summary of the existing env flow
2. dependency installed
3. `env.manifest.ts` added
4. package scripts updated
5. generated env outputs wired up
6. docs updated
7. verification commands run

Expected setup outputs:
- a concise pre-change audit covering env sources, loading points, validation, generated files, and main dev commands
- `env.manifest.ts` describing source env files, init templates, variables, and generated outputs
- repo scripts such as `env:init`, `env:generate`, `env:doctor`, `env:scan-secrets`, and `env:clear`
- generated app-specific env files for frontend/backend/mobile/runtime consumers
- a documented verification path using `keystone doctor`, `keystone scan-secrets`, and the repo's normal test/dev commands
Local file path prompt
Set up Keystone env management in this repository.

Install Keystone from this local path:
`file:/absolute/path/to/keystone`

Goals:
- inspect the existing environment-variable workflow first
- summarize the current env flow before making changes, including source files, loaders, generators, validation, and developer entrypoints
- add the Keystone package from the local file path above
- create an `env.manifest.ts` at the repo root
- preserve current developer workflows where possible, but route them through Keystone
- add scripts for `keystone init`, `keystone generate`, `keystone doctor`, `keystone scan-secrets`, and `keystone clear`
- mark secret env vars as `secret: true`
- mark client/browser/public outputs as `public: true`
- update `.gitignore` for generated env files
- update the repo README with the new env workflow
- run verification and report exactly what changed

Constraints:
- do not invent extra abstractions if the repo already has a clear env flow
- keep the migration incremental and compatible first
- prefer one canonical variable name per concept; use aliases only when truly needed
- do not expose secret vars to public outputs

What I want you to deliver:
1. a short audit summary of the existing env flow
2. dependency installed
3. `env.manifest.ts` added
4. package scripts updated
5. generated env outputs wired up
6. docs updated
7. verification commands run

Expected setup outputs:
- a concise pre-change audit covering env sources, loading points, validation, generated files, and main dev commands
- `env.manifest.ts` describing source env files, init templates, variables, and generated outputs
- repo scripts such as `env:init`, `env:generate`, `env:doctor`, `env:scan-secrets`, and `env:clear`
- generated app-specific env files for frontend/backend/mobile/runtime consumers
- a documented verification path using `keystone doctor`, `keystone scan-secrets`, and the repo's normal test/dev commands

Development

mise install
bun install
bun run build
bun run openapi:generate
bun test
bun run check

bun run smoke executes an end-to-end smoke test against a fixture workspace under fixtures/smoke-workspace/. It installs the package into a temporary sub-repo, runs the CLI, verifies generated outputs, and then clears them. That smoke test now also covers init and scan-secrets.

bun test runs focused unit tests for env parsing/formatting and manifest workflows like alias resolution, init, generate, clear, and secret scanning.

About

Manifest-driven environment tooling for multi-project workspaces

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages