Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
2e23bbe
docs: add DirectUrlAuth plugin testing documentation and CLI interact…
jimthompson5802 Sep 19, 2026
a64c4c5
docs: enhance DirectUrlAuth plugin documentation with integration det…
jimthompson5802 Sep 19, 2026
93193d3
docs: update CLI configuration options and enhance direct URL authent…
jimthompson5802 Sep 19, 2026
b99952c
docs: clarify direct URL authentication plugin options and improve do…
jimthompson5802 Sep 19, 2026
25a406b
docs: refine Direct URL auth plugin documentation for clarity and acc…
jimthompson5802 Sep 20, 2026
6b3276c
docs: remove local test environment section from Direct URL auth plug…
jimthompson5802 Sep 20, 2026
3c00dc2
docs: add illustrative code examples and infrastructure assumptions
jimthompson5802 Sep 20, 2026
c0e9053
docs: update illustrative code examples and clarify infrastructure as…
jimthompson5802 Sep 20, 2026
372271b
docs: update illustrative code section and clarify TypeScript require…
jimthompson5802 Sep 20, 2026
99f6e52
feat: dependencies for directUrlAuth documentation updates
jimthompson5802 Sep 21, 2026
8060c88
docs: clarify warranty disclaimer in authentication plugin documentation
jimthompson5802 Sep 21, 2026
09e89e0
docs: improve readability and clarity
jimthompson5802 Sep 22, 2026
4d8e565
docs: add diagram for non-CALM Hub repository integration in authenti…
jimthompson5802 Sep 23, 2026
98b1e21
docs: add sequence diagram for authentication flow in DirectUrlAuthPl…
jimthompson5802 Sep 23, 2026
f2c1c9e
docs: enhance diagram for End User Organization integration in direct…
jimthompson5802 Sep 23, 2026
14bf309
docs: update diagram to be explicit about calm artifact store
jimthompson5802 Sep 23, 2026
33418f8
docs: emphasize warranty disclaimer in CALM plugin documentation
jimthompson5802 Sep 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,15 +617,27 @@ Usage: calm init-config [options]
Create or update the CALM CLI configuration file (~/.calm.json).

Options:
--allowed-remote-hosts <hosts> Comma-separated list of trusted remote hosts to allow for direct URL loading
--calm-hub-url <url> URL to a trusted file location (e.g. CALMHub) to allow for direct URL loading of CALM documents
-h, --help display help for command
--allowed-remote-hosts <hosts> Comma-separated list of trusted remote hosts to allow for direct URL loading
--calm-hub-url <url> URL to a trusted file location (e.g. CALMHub) to allow for direct URL loading of CALM documents
--auth-plugin-path <path> Path to the CALM Hub authentication plugin
--direct-url-auth-module <path> Path to the direct URL authentication module
--direct-url-auth-config-path <path> Optional config path for the direct URL authentication module
--direct-url-auth-authenticated-hosts <hosts> Comma-separated hostnames requiring direct URL authentication
-h, --help display help for command
```

```shell
% calm init-config --calm-hub-url https://calmhub.example.com --allowed-remote-hosts raw.githubusercontent.com,calm.finos.org
calm init-config \
--calm-hub-url https://calmhub.example.com \
--allowed-remote-hosts raw.githubusercontent.com,calm.finos.org \
--auth-plugin-path ~/plugins/auth-plugin.js \
--direct-url-auth-module ~/plugins/direct-url-auth.js \
--direct-url-auth-config-path ~/plugins/direct-url-auth.config.json \
--direct-url-auth-authenticated-hosts protected.example.com,secure.example.com
```

The two host-list options trim whitespace, remove empty entries, and merge new hosts with the existing values without duplicates. Scalar options replace their existing values when supplied. Plugin and module paths are stored as provided and validated when the CLI uses them.

## Authentication plugins

The CLI supports an external authentication plugin to allow authentication to CalmHub in enterprise environments, where seamless auth will likely require specific logic.
Expand Down
69 changes: 69 additions & 0 deletions cli/src/cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1606,6 +1606,75 @@ describe('CLI Commands', () => {
});
});

describe('init-config command', () => {
it('saves all supported authentication options', async () => {
const saveCliConfig = vi.spyOn(cliConfigModule, 'saveCliConfig').mockResolvedValue(undefined);

await program.parseAsync([
'node', 'cli.js', 'init-config',
'--calm-hub-url', 'https://calmhub.example.com',
'--allowed-remote-hosts', 'schemas.example.com, calm.finos.org',
'--auth-plugin-path', '~/plugins/auth-plugin.js',
'--direct-url-auth-module', '~/plugins/direct-url-auth.js',
'--direct-url-auth-config-path', '~/plugins/direct-url-auth.config.json',
'--direct-url-auth-authenticated-hosts', 'protected.example.com, secure.example.com',
]);

expect(saveCliConfig).toHaveBeenCalledWith({
calmHubUrl: 'https://calmhub.example.com',
allowedRemoteHosts: ['schemas.example.com', 'calm.finos.org'],
authPluginPath: '~/plugins/auth-plugin.js',
directUrlAuthModule: '~/plugins/direct-url-auth.js',
directUrlAuthConfigPath: '~/plugins/direct-url-auth.config.json',
directUrlAuthAuthenticatedHosts: ['protected.example.com', 'secure.example.com'],
});
});

it('preserves existing values and deduplicates authenticated hosts', async () => {
vi.mocked(cliConfigModule.loadCliConfig).mockResolvedValue({
calmHubUrl: 'https://existing.example.com',
authPluginPath: '/existing/auth-plugin.js',
directUrlAuthModule: '/existing/direct-url-auth.js',
directUrlAuthAuthenticatedHosts: ['protected.example.com'],
});
const saveCliConfig = vi.spyOn(cliConfigModule, 'saveCliConfig').mockResolvedValue(undefined);

await program.parseAsync([
'node', 'cli.js', 'init-config',
'--direct-url-auth-authenticated-hosts', ' protected.example.com, secure.example.com, ',
]);

expect(saveCliConfig).toHaveBeenCalledWith({
calmHubUrl: 'https://existing.example.com',
authPluginPath: '/existing/auth-plugin.js',
directUrlAuthModule: '/existing/direct-url-auth.js',
directUrlAuthAuthenticatedHosts: ['protected.example.com', 'secure.example.com'],
});
});

it('replaces scalar values when they are supplied', async () => {
vi.mocked(cliConfigModule.loadCliConfig).mockResolvedValue({
authPluginPath: '/old/auth-plugin.js',
directUrlAuthModule: '/old/direct-url-auth.js',
directUrlAuthConfigPath: '/old/config.json',
});
const saveCliConfig = vi.spyOn(cliConfigModule, 'saveCliConfig').mockResolvedValue(undefined);

await program.parseAsync([
'node', 'cli.js', 'init-config',
'--auth-plugin-path', '/new/auth-plugin.js',
'--direct-url-auth-module', '/new/direct-url-auth.js',
'--direct-url-auth-config-path', '/new/config.json',
]);

expect(saveCliConfig).toHaveBeenCalledWith({
authPluginPath: '/new/auth-plugin.js',
directUrlAuthModule: '/new/direct-url-auth.js',
directUrlAuthConfigPath: '/new/config.json',
});
});
});

});

describe('parseDocumentLoaderConfig', () => {
Expand Down
22 changes: 22 additions & 0 deletions cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,10 @@ Example:
.description('Create or update the CALM CLI configuration file (~/.calm.json).')
.option('--allowed-remote-hosts <hosts>', 'Comma-separated list of trusted remote hosts to allow for direct URL loading')
.option('--calm-hub-url <url>', 'URL to a trusted file location (e.g. CALMHub) to allow for direct URL loading of CALM documents')
.option('--auth-plugin-path <path>', 'Path to the CALM Hub authentication plugin')
.option('--direct-url-auth-module <path>', 'Optional path to the direct URL authentication plugin module')
.option('--direct-url-auth-config-path <path>', 'Optional config path for the direct URL authentication plugin module')
.option('--direct-url-auth-authenticated-hosts <hosts>', 'If direct-url-auth-module is specified, this specifies a comma-separated list of hostnames requiring direct URL authentication plugin module')
.action(async (options) => {
const existingConfig = await cliConfig.loadCliConfig() ?? {};

Expand All @@ -454,6 +458,24 @@ Example:
existingConfig.calmHubUrl = options.calmHubUrl;
}

if (options.authPluginPath) {
existingConfig.authPluginPath = options.authPluginPath;
}

if (options.directUrlAuthModule) {
existingConfig.directUrlAuthModule = options.directUrlAuthModule;
}

if (options.directUrlAuthConfigPath) {
existingConfig.directUrlAuthConfigPath = options.directUrlAuthConfigPath;
}

if (options.directUrlAuthAuthenticatedHosts) {
const newHosts = (options.directUrlAuthAuthenticatedHosts as string).split(',').map((host: string) => host.trim()).filter(Boolean);
const existingHosts = existingConfig.directUrlAuthAuthenticatedHosts ?? [];
existingConfig.directUrlAuthAuthenticatedHosts = [...new Set([...existingHosts, ...newHosts])];
}

const configPath = cliConfig.getUserConfigLocation();
await cliConfig.saveCliConfig(existingConfig);
console.log(`βœ… Configuration saved to ${configPath}`);
Expand Down
33 changes: 33 additions & 0 deletions docs/docs/working-with-calm/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -776,3 +776,36 @@ All `hub` subcommands support a `-f, --format <format>` option with two choices:

- **`json`** _(default)_ β€” outputs the raw JSON response from CALM Hub. Suitable for piping into other tools or scripts.
- **`pretty`** β€” renders the output as a human-readable ASCII table. Available for `list` commands; for `push` and `pull` commands it formats the response in a more readable way.

---

## Interacting with non-CALM Hub repository

For an organization that does not require the full functionality of CALM Hub and only needs the ability to store CALM artifacts in a central repository, the CLI's `DirectUrlDocumentLoader` lets `calm validate` and `calm generate` fetch architectures, patterns, and standards directly from any HTTP(S) endpoint β€” for example a static file server or an internal artifact repository β€” without going through CALM Hub.

Depending on the authentication/authorization requirements for the non-CALM Hub repositories the following configurations in `~/.calm.json` are supported:

- Public, anonymous access allowed:
- **`allowedRemoteHosts`** is an allow-list of hostnames the CLI is permitted to fetch documents.
- Requires authenticated access:
- **`directUrlAuthModule`**: Path to the local JavaScript authentication plugin used for protected direct URLs.
- **`directUrlAuthConfigPath`**: Optional path passed to the plugin constructor for its own configuration.
- **`directUrlAuthAuthenticatedHosts`**: List of hostnames that require authentication headers.

:::note
Union of `allowedRemoteHosts` and `directUrlAuthAuthenticatedHosts` are hosts the `DirectDocumentLoader` are allowed to access.
:::

::::note
The `directUrlAuth` plugin mechanism exists to let end user organizations implement their own authentication/authorization method based on HTTP(S) headers. Building, configuring, and securing that plugin is the sole responsibility of the end user organization. The JavaScript plugin module exports a `default class` implementing a `getAuthHeaders(url, requestBody)` function.

**The CALM project provides this mechanism and documentation as-is, with no warranty or support**.

:::warning
The end user organization is solely responsible for security or correctness of the plugin implementation.
:::
::::

This plugin approach means the auth scheme is not fixed by the CLI itself β€” the end user supplied module can implement anything from a hard-coded header, to OAuth2 client-credentials, to a secret pulled from a vault at request time.

[DirectUrlAuth Plugin](directurl-auth-plugin) page provides technical details for writing and configuring the plugin.
Loading
Loading