{"_id":"@emdzej/config-service","name":"@emdzej/config-service","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@emdzej/config-service","version":"0.1.0","description":"Centralised read-only config service with placeholder resolution, env overlay, and JSON Schema validation","license":"MIT","repository":{"type":"git","url":"git+https://github.com/emdzej/config.git","directory":"apps/config-service"},"keywords":["config","configuration","cloud-native","spa","kubernetes"],"main":"dist/index.js","types":"dist/index.d.ts","bin":{"config-service":"dist/index.js"},"dependencies":{"express":"^5.1.0","jose":"^6.2.2","pino":"^10.3.1","pino-pretty":"^13.1.3","@emdzej/config-resolver":"0.1.0"},"devDependencies":{"@types/express":"^5.0.2","@types/node":"^20.11.25","tsx":"^4.19.0","typescript":"^5.4.2","vitest":"^4.1.4"},"engines":{"node":">=22"},"scripts":{"build":"tsc","start":"node dist/index.js","dev":"tsx watch src/index.ts","test":"vitest run","check-types":"tsc --noEmit"},"_id":"@emdzej/config-service@0.1.0","bugs":{"url":"https://github.com/emdzej/config/issues"},"homepage":"https://github.com/emdzej/config#readme","_integrity":"sha512-zqaF/IB+Wft00nSYtE2Y7f03oHwrJbUZrPnm55GVT6MSAor1d970BZuNmobP4NwLdjXIw0KDA/VGrbBc88+NbQ==","_resolved":"/private/var/folders/7h/x_w_580x4s9dq3sq11tpvzkwy3nbj8/T/d8a907a8e4e1fd743344ed177cb17df2/emdzej-config-service-0.1.0.tgz","_from":"file:emdzej-config-service-0.1.0.tgz","_nodeVersion":"22.22.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-zqaF/IB+Wft00nSYtE2Y7f03oHwrJbUZrPnm55GVT6MSAor1d970BZuNmobP4NwLdjXIw0KDA/VGrbBc88+NbQ==","shasum":"09a7e55f1f58d1f4d2bfb5952aa04fe8e82b7f0e","tarball":"https://registry.npmjs.org/@emdzej/config-service/-/config-service-0.1.0.tgz","fileCount":22,"unpackedSize":53146,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIC1pvHscrwWgxfHB3ggb/lE2lHhQR6z02NkvsclQj7cTAiEAwuSirqvPjLgckAoBHy9EuhkFfXvzZDblyLs1s5HLSTg="}]},"_npmUser":{"name":"emdzej","email":"michal@jaskolski.pro"},"directories":{},"maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/config-service_0.1.0_1776342097469_0.5936265401919101"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-16T12:21:37.300Z","0.1.0":"2026-04-16T12:21:37.609Z","modified":"2026-04-16T12:21:37.852Z"},"maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"description":"Centralised read-only config service with placeholder resolution, env overlay, and JSON Schema validation","homepage":"https://github.com/emdzej/config#readme","keywords":["config","configuration","cloud-native","spa","kubernetes"],"repository":{"type":"git","url":"git+https://github.com/emdzej/config.git","directory":"apps/config-service"},"bugs":{"url":"https://github.com/emdzej/config/issues"},"license":"MIT","readme":"# @emdzej/config-service\n\nLightweight HTTP service that serves resolved JSON configuration to SPAs and other clients that cannot read environment variables at runtime. Build once, run everywhere.\n\nPart of the [`@emdzej/config`](https://github.com/emdzej/config) monorepo.\n\n## Install\n\n### Global CLI\n\n```bash\nnpm i -g @emdzej/config-service\nconfig-service\n```\n\n### Docker\n\n```bash\ndocker pull ghcr.io/emdzej/config/config-service:latest\ndocker run -p 3100:3100 -v ./config:/app/config ghcr.io/emdzej/config/config-service\n```\n\n### Helm\n\n```bash\nhelm install config-service oci://ghcr.io/emdzej/config/charts/config-service\n```\n\nSee the [Helm chart](../../charts/config-service/) for full values reference.\n\n## Endpoints\n\n| Method | Path | Auth | Description |\n|--------|------|------|-------------|\n| `GET` | `/config/:app` | No | Resolved config for a single app |\n| `GET` | `/config` | No | All configs merged as `{ appName: config }` |\n| `GET` | `/config?apps=a,b` | No | Selective merge — only listed apps |\n| `GET` | `/health` | No | Health check with validation status |\n| `POST` | `/config` | Yes | Reload configs from disk |\n\n### GET /config/:app\n\nReturns the resolved configuration for a single app. The app name matches the JSON filename in `CONFIG_DIR` (e.g. `webapp.json` is served at `/config/webapp`).\n\n**404** — app not found (response includes `available` app list).\n**500** — resolution or validation error (response includes partial `config`).\n\n### GET /config\n\nReturns all configs merged into a single object keyed by app name.\n\nWith `?apps=webapp,api` query parameter, returns only the listed apps. Unknown names yield **207** with a `notFound` array and `available` list alongside the partial `config`.\n\n### GET /health\n\nReturns `200` when all configs are valid, `503` otherwise. Response includes per-app validation details, error counts, and `loadedAt` timestamp.\n\n### POST /config\n\nReloads all configs from disk. Protected by authentication (see below). Returns the same shape as `/health`.\n\n## Environment variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `CONFIG_DIR` | `./config` | Directory containing config JSON files |\n| `SCHEMA_DIR` | `./schemas` | Directory containing JSON Schema files |\n| `PORT` | `3100` | HTTP listen port |\n| `CORS_ORIGIN` | `*` | CORS `Access-Control-Allow-Origin` header. Set to `\"\"` to disable |\n| `STRICT` | `true` | Fail on unresolved placeholders (`false` to leave them as-is) |\n| `LOG_LEVEL` | `info` | Pino log level (`debug`, `info`, `warn`, `error`, `silent`) |\n| `AUTH_ISSUER` | — | OIDC issuer URL — enables JWT authentication on `POST /config` |\n| `AUTH_SECRET` | — | Shared secret — enables secret authentication on `POST /config` |\n| `AUTH_SCOPE` | `cfg` | Required JWT scope claim |\n\nConfig values support `${PLACEHOLDER}` syntax resolved by [`@emdzej/config-resolver`](../../packages/config-resolver/). Environment variables matching the pattern `APP__KEY__NESTED` override config values (double-underscore as path separator).\n\n## Authentication\n\nOnly `POST /config` is protected. GET endpoints are always public.\n\nDetection is automatic based on environment variables:\n\n| `AUTH_ISSUER` | `AUTH_SECRET` | Mode |\n|:---:|:---:|---|\n| set | — | **JWT** — Bearer token validated via OIDC discovery + JWKS |\n| — | set | **Secret** — `Authorization` header compared directly to secret |\n| set | set | **JWT** (issuer takes priority) |\n| — | — | **None** — no authentication enforced |\n\n### JWT mode\n\n```bash\ncurl -X POST http://localhost:3100/config \\\n  -H \"Authorization: Bearer <token>\"\n```\n\nThe service discovers the JWKS endpoint via `AUTH_ISSUER/.well-known/openid-configuration`, validates the token signature, expiry, and issuer, then checks that the `scope` claim contains `AUTH_SCOPE` (default `cfg`).\n\n### Secret mode\n\n```bash\ncurl -X POST http://localhost:3100/config \\\n  -H \"Authorization: my-secret\"\n```\n\nNo `Bearer` prefix — the header value is compared directly.\n\n## Config files\n\nPlace JSON files in `CONFIG_DIR`. Each file becomes an app:\n\n```\nconfig/\n  webapp.json    -> GET /config/webapp\n  api.json       -> GET /config/api\n```\n\nOptional JSON Schema files in `SCHEMA_DIR` with matching names validate configs on load:\n\n```\nschemas/\n  webapp.json    -> validates config/webapp.json\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-b57f10ed0c4e45441a064adfd0e0b543"}