@kitsy/cnos-docs 1.6.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,3 +16,32 @@ Only promoted browser-safe values are available here.
16
16
  Framework integrations such as `@kitsy/cnos-vite`, `@kitsy/cnos-next`, and `@kitsy/cnos-webpack` are responsible for embedding that browser payload during the build.
17
17
 
18
18
  The browser runtime does not fetch config over the network. It reads from data already embedded into the bundle/runtime globals by the framework integration, so it follows the same bundle and service-worker caching behavior as the rest of your frontend assets.
19
+
20
+ ## Cross-App Browser Config
21
+
22
+ Use the same runtime for shared browser-safe values such as sibling app origins, CDN roots, public feature flags, or analytics ids.
23
+
24
+ ```yaml
25
+ public:
26
+ promote:
27
+ - value.apps.docs.origin
28
+ - value.apps.console.origin
29
+ ```
30
+
31
+ ```ts
32
+ import cnos from '@kitsy/cnos/browser';
33
+
34
+ const docsOrigin = cnos('public.apps.docs.origin');
35
+ const consoleOrigin = cnos('public.apps.console.origin');
36
+ ```
37
+
38
+ This is intentionally simple:
39
+
40
+ 1. define the values in CNOS
41
+ 2. promote them to `public.*`
42
+ 3. install the framework integration
43
+ 4. read them in browser code with `@kitsy/cnos/browser`
44
+
45
+ You should not need a second app-specific browser config mechanism on top of CNOS.
46
+
47
+ Derived public values are supported too, but only when CNOS can resolve them safely at build time. If a promoted key depends on a server-only runtime namespace such as `process.*` or `request.*`, the browser/public build fails instead of emitting unstable data.
@@ -13,3 +13,44 @@ const runtime = await createCnos({
13
13
  profile: 'stage',
14
14
  });
15
15
  ```
16
+
17
+ Common options:
18
+
19
+ - `cwd`
20
+ - `root`
21
+ - `workspace`
22
+ - `profile`
23
+ - `globalRoot`
24
+ - `cacheMode`
25
+ - `cacheTtlSeconds`
26
+ - `processEnv`
27
+
28
+ `root` can be a local path or a remote git root URI:
29
+
30
+ ```ts
31
+ const runtime = await createCnos({
32
+ root: 'git+https://github.com/org/config.git#v2.1.0',
33
+ workspace: 'travel',
34
+ });
35
+ ```
36
+
37
+ Derived values and runtime namespaces work through the explicit runtime too:
38
+
39
+ ```ts
40
+ import { createCnos } from '@kitsy/cnos/configure';
41
+
42
+ const runtime = await createCnos({
43
+ cwd: process.cwd(),
44
+ profile: 'prod',
45
+ });
46
+
47
+ runtime.registerRuntimeProvider('request', (key) => {
48
+ if (key === 'headers.host') {
49
+ return currentRequest?.headers.host;
50
+ }
51
+
52
+ return undefined;
53
+ });
54
+
55
+ const origin = runtime.value('app.origin');
56
+ ```
@@ -36,6 +36,7 @@ The runtime currently exposes:
36
36
  - `cnos.format(message)`
37
37
  - `cnos.log(message)`
38
38
  - `cnos.loadProjection(path)`
39
+ - `cnos.registerRuntimeProvider(namespace, provider)`
39
40
  - `cnos.refreshSecrets()`
40
41
  - `cnos.refreshSecret(key)`
41
42
 
@@ -67,6 +68,49 @@ await cnos.loadProjection('./.cnos-server.json');
67
68
  await cnos.ready();
68
69
  ```
69
70
 
71
+ ## Derived Values
72
+
73
+ Derived values resolve transparently through the same read APIs.
74
+
75
+ ```yaml
76
+ app:
77
+ origin:
78
+ $derive: "${value.app.protocol}://${value.app.host}:${value.app.port}"
79
+ ```
80
+
81
+ ```ts
82
+ const origin = cnos.value('app.origin');
83
+ ```
84
+
85
+ Config-only derivations are cached for the active runtime. Runtime-dependent derivations stay live:
86
+
87
+ ```yaml
88
+ server:
89
+ effective_port:
90
+ $derive:
91
+ expr: "coalesce(process.env.PORT, value.server.default_port, '3000')"
92
+ ```
93
+
94
+ ```ts
95
+ const port = cnos.value('server.effective_port');
96
+ ```
97
+
98
+ ## Runtime Providers
99
+
100
+ Declare custom runtime namespaces in the manifest, then register a provider at runtime.
101
+
102
+ ```ts
103
+ cnos.registerRuntimeProvider('request', (key) => {
104
+ if (key === 'headers.host') {
105
+ return currentRequest?.headers.host;
106
+ }
107
+
108
+ return undefined;
109
+ });
110
+ ```
111
+
112
+ That lets server-side derived values depend on live request, session, or app-framework context without adding a second config layer.
113
+
70
114
  ## Typed values
71
115
 
72
116
  CNOS values are not limited to strings. If the underlying config stores numbers, booleans, arrays, or objects, runtime reads return those values as-is.
@@ -13,7 +13,7 @@ cnos build browser --to .cnos-browser.json
13
13
  cnos build env --profile local --to .env.local
14
14
  cnos build env --profile stage --to .env.stage
15
15
  cnos build public --framework vite --to .env.production
16
- cnos build env --profile local-domain --format docker-env --to .docker/runtime/current.env
16
+ cnos build env --profile local-domain --format docker-env --to .env.local-domain
17
17
  ```
18
18
 
19
19
  Targets:
@@ -0,0 +1,28 @@
1
+ ---
2
+ title: cnos cache
3
+ description: Inspect and manage cached remote CNOS roots.
4
+ ---
5
+
6
+ # cnos cache
7
+
8
+ Use `cache` when a package anchor points at a remote root such as a git-backed config repo.
9
+
10
+ ```bash
11
+ cnos cache list
12
+ cnos cache clear
13
+ cnos cache clear git+https://github.com/org/config.git#v2.1.0
14
+ cnos cache refresh
15
+ cnos cache refresh git+ssh://git@github.com/org/config.git#main
16
+ ```
17
+
18
+ Subcommands:
19
+
20
+ - `cache list` shows cached remote roots, resolved commits, immutability, and size
21
+ - `cache clear` removes all cached roots or one specific cached root
22
+ - `cache refresh` forces a re-fetch for the current remote root or a specific git root URI
23
+
24
+ Notes:
25
+
26
+ - immutable refs such as semantic-version tags and commit SHAs stay cached permanently
27
+ - mutable refs such as branches are refreshed based on CNOS cache policy
28
+ - remote roots are read-only when consumed through CNOS
@@ -9,3 +9,31 @@ description: Inspect resolved values, provenance, and overrides.
9
9
  cnos inspect value.app.name
10
10
  cnos inspect secret.app.token --reveal
11
11
  ```
12
+
13
+ `cnos inspect` also shows derived metadata.
14
+
15
+ ```bash
16
+ cnos inspect value.app.origin
17
+ ```
18
+
19
+ Typical output includes:
20
+
21
+ - resolved value
22
+ - expression or template source
23
+ - dependency keys
24
+ - whether the value is runtime-dependent
25
+ - which runtime namespaces it depends on
26
+
27
+ Example:
28
+
29
+ ```text
30
+ key: value.app.origin
31
+ value: https://api.kitsy.ai:443
32
+ derivedType: template
33
+ derivedExpression: ${value.app.protocol}://${value.app.host}:${value.app.port}
34
+ runtimeDependent: false
35
+ dependencies:
36
+ - value.app.host
37
+ - value.app.port
38
+ - value.app.protocol
39
+ ```
@@ -12,6 +12,22 @@ cnos value list --prefix app.
12
12
  cnos value delete app.name
13
13
  ```
14
14
 
15
+ ## Derived values
16
+
17
+ Use `--derive` for template shorthand:
18
+
19
+ ```bash
20
+ cnos value set app.origin --derive '${value.app.protocol}://${value.app.host}'
21
+ ```
22
+
23
+ Use `--derive --expr` for expression form:
24
+
25
+ ```bash
26
+ cnos value set app.effective_port --derive --expr "coalesce(process.env.PORT, value.app.default_port, '3000')"
27
+ ```
28
+
29
+ CNOS stores these as first-class derived values, not as opaque strings.
30
+
15
31
  ## Typed input
16
32
 
17
33
  `cnos value set` parses the input as YAML before writing it. That means you can store more than strings.
@@ -46,3 +62,4 @@ cnos read value.app.name
46
62
  - Values are private by default.
47
63
  - To expose a value to browser/public surfaces, use `cnos promote`.
48
64
  - To expose a value as shell env, map it through `envMapping.explicit` or `cnos promote --to env`.
65
+ - Derived values can be authored only under writable data namespaces such as `value.*` and custom writable data namespaces.
@@ -30,6 +30,31 @@ const port = cnos.value('server.port');
30
30
  const token = cnos.secret('app.token');
31
31
  ```
32
32
 
33
+ Derived server config stays inside CNOS too:
34
+
35
+ ```yaml
36
+ server:
37
+ effective_port:
38
+ $derive:
39
+ expr: "coalesce(process.env.PORT, value.server.default_port, '3000')"
40
+ ```
41
+
42
+ ```ts
43
+ const port = cnos.value('server.effective_port');
44
+ ```
45
+
46
+ If you need live request or session context, declare a runtime namespace and register a provider:
47
+
48
+ ```ts
49
+ cnos.registerRuntimeProvider('request', (key) => {
50
+ if (key === 'headers.host') {
51
+ return currentRequest?.headers.host;
52
+ }
53
+
54
+ return undefined;
55
+ });
56
+ ```
57
+
33
58
  Env export bridge:
34
59
 
35
60
  ```bash
@@ -0,0 +1,130 @@
1
+ ---
2
+ title: Derived Values
3
+ description: Compute config from other CNOS keys and live runtime namespaces.
4
+ ---
5
+
6
+ # Derived Values
7
+
8
+ Derived values let you keep config composition inside CNOS instead of re-implementing it in app code.
9
+
10
+ Template shorthand:
11
+
12
+ ```yaml
13
+ app:
14
+ origin:
15
+ $derive: "${value.app.protocol}://${value.app.host}:${value.app.port}"
16
+ ```
17
+
18
+ Expression form:
19
+
20
+ ```yaml
21
+ app:
22
+ effective_port:
23
+ $derive:
24
+ expr: "coalesce(process.env.PORT, value.app.default_port, '3000')"
25
+ ```
26
+
27
+ ## Write from the CLI
28
+
29
+ ```bash
30
+ cnos value set app.origin --derive '${value.app.protocol}://${value.app.host}'
31
+ cnos value set app.effective_port --derive --expr "coalesce(process.env.PORT, value.app.default_port, '3000')"
32
+ ```
33
+
34
+ ## Built-ins
35
+
36
+ Supported functions:
37
+
38
+ - `concat(...)`
39
+ - `coalesce(...)`
40
+ - `when(condition, thenValue, elseValue)`
41
+ - `exists(ref)`
42
+ - `eq(a, b)`
43
+ - `ne(a, b)`
44
+
45
+ The language is intentionally small and safe. There is no arbitrary JavaScript execution.
46
+
47
+ ## Runtime Namespaces
48
+
49
+ `process.*` is the built-in runtime namespace. It stays live between reads.
50
+
51
+ ```yaml
52
+ app:
53
+ effective_port:
54
+ $derive:
55
+ expr: "coalesce(process.env.PORT, value.app.default_port, '3000')"
56
+ ```
57
+
58
+ Custom runtime namespaces are declared in the manifest and populated by the host app:
59
+
60
+ ```yaml
61
+ namespaces:
62
+ runtime:
63
+ request:
64
+ description: HTTP request context
65
+ server_only: true
66
+ ```
67
+
68
+ ```ts
69
+ import cnos from '@kitsy/cnos';
70
+
71
+ await cnos.ready();
72
+
73
+ cnos.registerRuntimeProvider('request', (key) => {
74
+ if (key === 'headers.host') {
75
+ return currentRequest?.headers.host;
76
+ }
77
+
78
+ return undefined;
79
+ });
80
+ ```
81
+
82
+ ## Caching Rules
83
+
84
+ - config-only derivations are cached once per resolution pass
85
+ - runtime-dependent derivations are never cached
86
+
87
+ That means `value.app.origin` is stable until the next `createCnos()` or `cnos.ready()`, while `value.app.effective_port` can change between reads when `process.env.PORT` changes.
88
+
89
+ ## Browser and Server Behavior
90
+
91
+ Browser/public outputs always need concrete values.
92
+
93
+ - config-only derived values can be promoted and embedded normally
94
+ - runtime-dependent derived values that rely on server-only namespaces are rejected for browser/public builds
95
+
96
+ Server projections split the result:
97
+
98
+ - `values` contains concrete non-secret values, including config-only derivations
99
+ - `derived` contains live runtime-dependent formulas
100
+ - `runtimeNamespaces` lists which runtime providers the server runtime needs
101
+
102
+ ## Use Cases
103
+
104
+ Cross-app browser links:
105
+
106
+ ```yaml
107
+ apps:
108
+ cnos:
109
+ origin:
110
+ $derive: "${value.platform.protocol}://${value.apps.cnos.host}"
111
+
112
+ public:
113
+ promote:
114
+ - value.apps.cnos.origin
115
+ ```
116
+
117
+ ```ts
118
+ import cnos from '@kitsy/cnos/browser';
119
+
120
+ const cnosOrigin = cnos('public.apps.cnos.origin');
121
+ ```
122
+
123
+ Server fallback port:
124
+
125
+ ```yaml
126
+ server:
127
+ port:
128
+ $derive:
129
+ expr: "coalesce(process.env.PORT, value.server.default_port, '3000')"
130
+ ```
@@ -40,6 +40,58 @@ console.log(cnos('public.app.apiBaseUrl'));
40
40
  console.log(import.meta.env.VITE_APP_API_BASE_URL);
41
41
  ```
42
42
 
43
+ Cross-app browser links work the same way. Put the browser-safe origins in CNOS, promote them, and let the Vite plugin handle the embedding:
44
+
45
+ ```yaml
46
+ public:
47
+ promote:
48
+ - value.apps.main.origin
49
+ - value.apps.cnos.origin
50
+ - value.apps.coop.origin
51
+ ```
52
+
53
+ ```yaml
54
+ apps:
55
+ main:
56
+ origin: https://kitsy.ai
57
+ cnos:
58
+ origin: https://cnos.kitsy.ai
59
+ coop:
60
+ origin: https://coop.kitsy.ai
61
+ ```
62
+
63
+ Then in UI code:
64
+
65
+ ```ts
66
+ import cnos from '@kitsy/cnos/browser';
67
+
68
+ const cnosOrigin = cnos('public.apps.cnos.origin');
69
+ const coopOrigin = cnos('public.apps.coop.origin');
70
+ ```
71
+
72
+ That is the point of the integration. You promote once, keep using the same logical reads in UI code, and `@kitsy/cnos-vite` makes sure the values are already present in the browser bundle.
73
+
74
+ Derived public values work the same way as long as they are build-safe:
75
+
76
+ ```yaml
77
+ apps:
78
+ cnos:
79
+ origin:
80
+ $derive: "${value.platform.protocol}://${value.apps.cnos.host}"
81
+
82
+ public:
83
+ promote:
84
+ - value.apps.cnos.origin
85
+ ```
86
+
87
+ ```ts
88
+ import cnos from '@kitsy/cnos/browser';
89
+
90
+ const cnosOrigin = cnos('public.apps.cnos.origin');
91
+ ```
92
+
93
+ If a promoted derived value depends on a server-only runtime namespace such as `process.*` or a custom `request.*`, CNOS rejects the browser/public build instead of leaking an unstable value.
94
+
43
95
  Migration path:
44
96
 
45
97
  1. keep `VITE_*` working through generated env files
@@ -0,0 +1,86 @@
1
+ ---
2
+ title: Remote Roots
3
+ description: Point .cnosrc.yml at a remote git-backed CNOS root and consume it through the local cache.
4
+ ---
5
+
6
+ # Remote Roots
7
+
8
+ `.cnosrc.yml` can point at either a local `.cnos` directory or a remote git-backed root.
9
+
10
+ Local:
11
+
12
+ ```yaml
13
+ root: ./.cnos
14
+ ```
15
+
16
+ Remote:
17
+
18
+ ```yaml
19
+ root: git+https://github.com/org/config.git#v2.1.0
20
+ workspace: travel
21
+ ```
22
+
23
+ Remote roots are resolved into the CNOS cache, then loaded exactly like a local manifest root.
24
+
25
+ ## Supported URIs
26
+
27
+ Recommended production form:
28
+
29
+ ```yaml
30
+ root: git+https://github.com/org/config.git#v2.1.0
31
+ ```
32
+
33
+ Private repo over SSH:
34
+
35
+ ```yaml
36
+ root: git+ssh://git@github.com/org/private-config.git#main
37
+ ```
38
+
39
+ Subpath inside the repo:
40
+
41
+ ```yaml
42
+ root: git+https://github.com/org/monorepo.git#v2.1.0:config/.cnos
43
+ ```
44
+
45
+ ## Cache Behavior
46
+
47
+ - semantic-version tags and commit SHAs are treated as immutable
48
+ - branch refs are treated as mutable
49
+ - runtime reads use a cache TTL
50
+ - `cnos build` refreshes mutable refs before building artifacts
51
+ - `cnos cache refresh` forces a re-fetch when needed
52
+
53
+ Inspect the cache:
54
+
55
+ ```bash
56
+ cnos cache list
57
+ ```
58
+
59
+ Override TTL for one command when needed:
60
+
61
+ ```bash
62
+ cnos read value.app.name --cache-ttl 0
63
+ cnos watch --cache-ttl 15 -- node server.js
64
+ ```
65
+
66
+ ## Read-Only Model
67
+
68
+ Remote roots are consumed as read-only config.
69
+
70
+ These work:
71
+
72
+ ```bash
73
+ cnos read value.app.name
74
+ cnos build env --to .env.stage
75
+ cnos run -- node server.js
76
+ ```
77
+
78
+ These are blocked:
79
+
80
+ ```bash
81
+ cnos value set app.name demo
82
+ cnos promote value.app.name --to public
83
+ cnos vault create default
84
+ ```
85
+
86
+ The right workflow is to edit the source config repo and then let consumers refresh the cached root.
@@ -22,6 +22,13 @@ root: ../../.cnos
22
22
  workspace: travel
23
23
  ```
24
24
 
25
+ Child app from a remote config repo:
26
+
27
+ ```yaml
28
+ root: git+https://github.com/org/config.git#v2.1.0
29
+ workspace: travel
30
+ ```
31
+
25
32
  `.cnosrc.yml` is the only discovery anchor. CNOS does not walk upward looking for `.cnos` directories. It looks for `.cnosrc.yml` within a bounded package-root search window, then resolves the real `.cnos` root from there.
26
33
 
27
34
  Typical manifest shape:
@@ -12,6 +12,7 @@ Primary namespaces:
12
12
  - `meta.*`
13
13
  - `process.*` for server-only ambient runtime state
14
14
  - custom data namespaces such as `flags.*` or `config.*` when declared in `.cnos/cnos.yml`
15
+ - custom runtime namespaces declared under `namespaces.runtime`
15
16
 
16
17
  Derived surfaces:
17
18
 
@@ -35,3 +36,36 @@ Custom data namespaces can be:
35
36
  - `process.env.PATH`
36
37
 
37
38
  `process.*` cannot be promoted or exported.
39
+
40
+ ## Runtime Namespaces
41
+
42
+ Runtime namespaces are not populated from config files. They are declared in the manifest and populated by the host runtime.
43
+
44
+ ```yaml
45
+ namespaces:
46
+ runtime:
47
+ request:
48
+ description: HTTP request context
49
+ server_only: true
50
+ ```
51
+
52
+ Use them from derived values:
53
+
54
+ ```yaml
55
+ app:
56
+ current_host:
57
+ $derive:
58
+ expr: "coalesce(request.headers.host, value.app.default_host)"
59
+ ```
60
+
61
+ And register providers in server code:
62
+
63
+ ```ts
64
+ cnos.registerRuntimeProvider('request', (key) => {
65
+ if (key === 'headers.host') {
66
+ return currentRequest?.headers.host;
67
+ }
68
+
69
+ return undefined;
70
+ });
71
+ ```
package/manifest.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  product: cnos
2
2
  title: CNOS Documentation
3
3
  tagline: Configuration orchestration for apps, monorepos, and deployment pipelines
4
- version: "1.5"
4
+ version: "1.7"
5
5
 
6
6
  sidebar:
7
7
  - group: Getting Started
@@ -33,8 +33,12 @@ sidebar:
33
33
  label: Containers and Actions
34
34
  - path: guides/workspaces
35
35
  label: Workspaces and Monorepos
36
+ - path: guides/remote-roots
37
+ label: Remote Roots
36
38
  - path: guides/profiles
37
39
  label: Profiles and Environments
40
+ - path: guides/derived-values
41
+ label: Derived Values
38
42
  - path: guides/secrets
39
43
  label: Secrets and Vaults
40
44
  - path: guides/migration
@@ -63,6 +67,8 @@ sidebar:
63
67
  label: cnos export
64
68
  - path: cli/build
65
69
  label: cnos build env
70
+ - path: cli/cache
71
+ label: cnos cache
66
72
  - path: cli/dev
67
73
  label: cnos dev env
68
74
  - path: cli/run
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitsy/cnos-docs",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
4
4
  "description": "Source-of-truth CNOS documentation content for Astro Starlight and other static docs consumers.",
5
5
  "type": "module",
6
6
  "exports": {