@bothat-io/molenkopf 0.1.2

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.
Files changed (147) hide show
  1. package/.env.example +2 -0
  2. package/LICENSE +21 -0
  3. package/README.md +199 -0
  4. package/SECURITY.md +36 -0
  5. package/bin/launcher.js +76 -0
  6. package/bin/molenkopf.js +4 -0
  7. package/docs/DEPLOYMENT.md +104 -0
  8. package/docs/MOLENKOPF_PLUGIN_API.md +113 -0
  9. package/docs/MOLENKOPF_PROVIDER_ENV.md +123 -0
  10. package/docs/MOLENKOPF_USAGE.md +195 -0
  11. package/docs/PRODUCT_INTENT.md +36 -0
  12. package/docs/THREAT_MODEL.md +94 -0
  13. package/molenkopf.config.example.json +68 -0
  14. package/package.json +98 -0
  15. package/packages/core/src/auth/password.ts +47 -0
  16. package/packages/core/src/auth/session.ts +64 -0
  17. package/packages/core/src/ci/ci-mode.ts +71 -0
  18. package/packages/core/src/compression/content-classifier.ts +25 -0
  19. package/packages/core/src/compression/context-compressor.ts +48 -0
  20. package/packages/core/src/compression/json-compressor.ts +54 -0
  21. package/packages/core/src/compression/log-compressor.ts +32 -0
  22. package/packages/core/src/compression/operational-block-compressor.ts +43 -0
  23. package/packages/core/src/compression/stacktrace-compressor.ts +23 -0
  24. package/packages/core/src/config/config-policies.ts +146 -0
  25. package/packages/core/src/config/molenkopf-config.ts +137 -0
  26. package/packages/core/src/config/provider-config.ts +139 -0
  27. package/packages/core/src/events/event-bus.ts +88 -0
  28. package/packages/core/src/identity/api-keys.ts +149 -0
  29. package/packages/core/src/identity/budget.ts +51 -0
  30. package/packages/core/src/identity/db.ts +68 -0
  31. package/packages/core/src/identity/identity-store.ts +175 -0
  32. package/packages/core/src/identity/identity-validation.ts +102 -0
  33. package/packages/core/src/identity/key-permissions.ts +18 -0
  34. package/packages/core/src/identity/pricing.ts +11 -0
  35. package/packages/core/src/identity/types.ts +87 -0
  36. package/packages/core/src/identity/usage-snapshot.ts +116 -0
  37. package/packages/core/src/manifest/audit-activity.ts +74 -0
  38. package/packages/core/src/manifest/audit-metrics.ts +7 -0
  39. package/packages/core/src/manifest/audit-safety.ts +113 -0
  40. package/packages/core/src/manifest/audit-store.ts +189 -0
  41. package/packages/core/src/manifest/audit-summary.ts +184 -0
  42. package/packages/core/src/manifest/usage-meter.ts +105 -0
  43. package/packages/core/src/memory/memory-extractor.ts +57 -0
  44. package/packages/core/src/memory/memory-graph.ts +55 -0
  45. package/packages/core/src/pipeline/json-string-spans.ts +143 -0
  46. package/packages/core/src/pipeline/openai-request-rewriter.ts +66 -0
  47. package/packages/core/src/plugins/builtin-plugin-descriptors.ts +10 -0
  48. package/packages/core/src/plugins/builtin-plugin-modules.ts +9 -0
  49. package/packages/core/src/plugins/plugin-api.ts +96 -0
  50. package/packages/core/src/plugins/plugin-catalog.ts +42 -0
  51. package/packages/core/src/plugins/plugin-descriptor.ts +51 -0
  52. package/packages/core/src/plugins/plugin-sdk.ts +47 -0
  53. package/packages/core/src/plugins/static-pipeline.ts +5 -0
  54. package/packages/core/src/profiles/profile-router.ts +45 -0
  55. package/packages/core/src/providers/provider-catalog.ts +186 -0
  56. package/packages/core/src/routing/distribution.ts +31 -0
  57. package/packages/core/src/security/secret-redactor.ts +139 -0
  58. package/packages/core/src/security/target-policy.ts +61 -0
  59. package/packages/core/src/storage/local-paths.ts +6 -0
  60. package/packages/core/src/storage/private-state.ts +30 -0
  61. package/packages/core/src/storage/purge-dir.ts +10 -0
  62. package/packages/core/src/store/retrieval-store.ts +114 -0
  63. package/packages/core/src/utils/hash.ts +9 -0
  64. package/packages/core/src/utils/text.ts +18 -0
  65. package/packages/core/src/utils/tokens.ts +3 -0
  66. package/packages/dashboard/dist/assets/index-B_aSPgHx.js +11 -0
  67. package/packages/dashboard/dist/assets/index-D6z2TEL2.css +1 -0
  68. package/packages/dashboard/dist/favicon.png +0 -0
  69. package/packages/dashboard/dist/index.html +15 -0
  70. package/packages/dashboard/dist/molenkopf-logo.png +0 -0
  71. package/packages/dashboard/public/favicon.png +0 -0
  72. package/packages/dashboard/public/molenkopf-logo.png +0 -0
  73. package/packages/plugins/context-compressor-plugin/descriptor.ts +19 -0
  74. package/packages/plugins/context-compressor-plugin/page.html +191 -0
  75. package/packages/plugins/context-compressor-plugin/plugin.ts +40 -0
  76. package/packages/plugins/obsidian-graph-plugin/descriptor.ts +19 -0
  77. package/packages/plugins/obsidian-graph-plugin/page.html +68 -0
  78. package/packages/plugins/obsidian-graph-plugin/plugin.ts +27 -0
  79. package/packages/plugins/shared/audit-projects.ts +32 -0
  80. package/packages/proxy/src/cli/args.ts +34 -0
  81. package/packages/proxy/src/cli/config-loader.ts +43 -0
  82. package/packages/proxy/src/cli/env-file.ts +43 -0
  83. package/packages/proxy/src/cli/main.ts +132 -0
  84. package/packages/proxy/src/cli/profile-server.ts +176 -0
  85. package/packages/proxy/src/cli/target.ts +7 -0
  86. package/packages/proxy/src/http/agent-drafts.ts +103 -0
  87. package/packages/proxy/src/http/agent-router.ts +69 -0
  88. package/packages/proxy/src/http/audit-view.ts +15 -0
  89. package/packages/proxy/src/http/auth-state.ts +44 -0
  90. package/packages/proxy/src/http/budget-gate.ts +45 -0
  91. package/packages/proxy/src/http/budget-warnings.ts +7 -0
  92. package/packages/proxy/src/http/cli-stream-response.ts +51 -0
  93. package/packages/proxy/src/http/client-identity.ts +51 -0
  94. package/packages/proxy/src/http/communication-graph.ts +139 -0
  95. package/packages/proxy/src/http/control-plane-guard.ts +56 -0
  96. package/packages/proxy/src/http/dashboard-assets.ts +115 -0
  97. package/packages/proxy/src/http/encoded-usage-meter.ts +32 -0
  98. package/packages/proxy/src/http/header-utils.ts +65 -0
  99. package/packages/proxy/src/http/identity-id.ts +11 -0
  100. package/packages/proxy/src/http/local-api-agent-actions.ts +17 -0
  101. package/packages/proxy/src/http/local-api-auth.ts +120 -0
  102. package/packages/proxy/src/http/local-api-consumer-actions.ts +20 -0
  103. package/packages/proxy/src/http/local-api-identity.ts +194 -0
  104. package/packages/proxy/src/http/local-api-io.ts +82 -0
  105. package/packages/proxy/src/http/local-api-keys.ts +126 -0
  106. package/packages/proxy/src/http/local-api-pipeline.ts +41 -0
  107. package/packages/proxy/src/http/local-api-plugin-actions.ts +31 -0
  108. package/packages/proxy/src/http/local-api-provider-actions.ts +181 -0
  109. package/packages/proxy/src/http/local-api-retention.ts +28 -0
  110. package/packages/proxy/src/http/local-api-runtime-auth.ts +119 -0
  111. package/packages/proxy/src/http/local-api-scope.ts +47 -0
  112. package/packages/proxy/src/http/local-api-state.ts +180 -0
  113. package/packages/proxy/src/http/local-api.ts +166 -0
  114. package/packages/proxy/src/http/password-policy.ts +5 -0
  115. package/packages/proxy/src/http/plugin-data.ts +38 -0
  116. package/packages/proxy/src/http/plugin-host.ts +87 -0
  117. package/packages/proxy/src/http/plugin-modules.ts +1 -0
  118. package/packages/proxy/src/http/plugin-page-loader.ts +24 -0
  119. package/packages/proxy/src/http/plugin-pipeline.ts +125 -0
  120. package/packages/proxy/src/http/provider-access.ts +33 -0
  121. package/packages/proxy/src/http/provider-http-test.ts +133 -0
  122. package/packages/proxy/src/http/provider-input.ts +39 -0
  123. package/packages/proxy/src/http/provider-routing-snapshot.ts +28 -0
  124. package/packages/proxy/src/http/provider-test.ts +149 -0
  125. package/packages/proxy/src/http/proxy-identity.ts +78 -0
  126. package/packages/proxy/src/http/public-bind.ts +8 -0
  127. package/packages/proxy/src/http/request-finish.ts +62 -0
  128. package/packages/proxy/src/http/request-path.ts +8 -0
  129. package/packages/proxy/src/http/request-policy.ts +46 -0
  130. package/packages/proxy/src/http/runtime-auth-proof.ts +55 -0
  131. package/packages/proxy/src/http/runtime-auth-registry.ts +105 -0
  132. package/packages/proxy/src/http/runtime-settings.ts +199 -0
  133. package/packages/proxy/src/http/runtime-state.ts +198 -0
  134. package/packages/proxy/src/http/server-io.ts +80 -0
  135. package/packages/proxy/src/http/server-types.ts +17 -0
  136. package/packages/proxy/src/http/server.ts +190 -0
  137. package/packages/proxy/src/http/session-secret.ts +19 -0
  138. package/packages/proxy/src/http/streaming-proxy.ts +88 -0
  139. package/packages/proxy/src/http/usage-accounting.ts +100 -0
  140. package/packages/proxy/src/http/usage-restore.ts +15 -0
  141. package/packages/proxy/src/runtime/cli-diagnostics.ts +64 -0
  142. package/packages/proxy/src/runtime/cli-env.ts +22 -0
  143. package/packages/proxy/src/runtime/cli-executor.ts +134 -0
  144. package/packages/proxy/src/runtime/cli-provider.ts +162 -0
  145. package/packages/proxy/src/runtime/cli-request.ts +79 -0
  146. package/packages/proxy/src/runtime/codex-runtime-config.ts +37 -0
  147. package/packages/proxy/src/runtime/runtime-profile.ts +170 -0
@@ -0,0 +1,123 @@
1
+ # Molenkopf Provider ENV Setup
2
+
3
+ This documents environment-based provider setup. The JSON config path for
4
+ providers, agent bindings, plugin policies, and team distribution is
5
+ [MOLENKOPF_JSON_CONFIG_PLAN.md](MOLENKOPF_JSON_CONFIG_PLAN.md).
6
+
7
+ Use this when you want several upstream provider profiles in one local
8
+ Molenkopf instance. Credentials stay in environment variables. Molenkopf only
9
+ stores and displays the variable names.
10
+
11
+ ## Do Not Use
12
+
13
+ - Do not paste Claude, ChatGPT, Codex, or browser login/session tokens here.
14
+ - Do not commit a file with real keys.
15
+ - Do not give employees upstream provider keys. Later they get Molenkopf tokens.
16
+
17
+ Use provider API keys for API routing:
18
+
19
+ - OpenAI API key: `OPENAI_API_KEY` or your own named env key.
20
+ - Anthropic/Claude API key: `ANTHROPIC_API_KEY` or your own named env key.
21
+ - Codex CLI / Claude CLI login sessions are supported through JSON config and
22
+ runtime-auth import flows. They are not configured through env provider blocks.
23
+
24
+ ## Single Built-In Profiles
25
+
26
+ PowerShell:
27
+
28
+ ```powershell
29
+ Copy-Item .env.example .env
30
+ # Edit .env and set MOLENKOPF_SESSION_SECRET.
31
+ $env:OPENAI_API_KEY = "replace-with-openai-api-key"
32
+ $env:ANTHROPIC_API_KEY = "replace-with-anthropic-api-key"
33
+ npm run dev
34
+ ```
35
+
36
+ Then open:
37
+
38
+ ```text
39
+ http://127.0.0.1:8787/__molenkopf/dashboard
40
+ ```
41
+
42
+ Built-in provider IDs:
43
+
44
+ ```text
45
+ openai-env
46
+ anthropic-env
47
+ ollama-local
48
+ lmstudio-local
49
+ ```
50
+
51
+ ## Multiple Provider Profiles
52
+
53
+ Use `MOLENKOPF_PROVIDER_IDS` plus one block per ID. The ID is converted to an
54
+ env suffix by uppercasing and replacing `-` with `_`.
55
+
56
+ Example:
57
+
58
+ ```powershell
59
+ $env:MOLENKOPF_PROVIDER_IDS = "openai-main,openai-backup,claude-main,claude-work"
60
+
61
+ $env:MOLENKOPF_PROVIDER_OPENAI_MAIN_NAME = "OpenAI Main"
62
+ $env:MOLENKOPF_PROVIDER_OPENAI_MAIN_TARGET = "https://api.openai.com/v1"
63
+ $env:MOLENKOPF_PROVIDER_OPENAI_MAIN_CREDENTIAL_ENV = "OPENAI_MAIN_API_KEY"
64
+ $env:OPENAI_MAIN_API_KEY = "replace-with-openai-main-api-key"
65
+
66
+ $env:MOLENKOPF_PROVIDER_OPENAI_BACKUP_NAME = "OpenAI Backup"
67
+ $env:MOLENKOPF_PROVIDER_OPENAI_BACKUP_TARGET = "https://api.openai.com/v1"
68
+ $env:MOLENKOPF_PROVIDER_OPENAI_BACKUP_CREDENTIAL_ENV = "OPENAI_BACKUP_API_KEY"
69
+ $env:OPENAI_BACKUP_API_KEY = "replace-with-openai-backup-api-key"
70
+
71
+ $env:MOLENKOPF_PROVIDER_CLAUDE_MAIN_NAME = "Claude Main"
72
+ $env:MOLENKOPF_PROVIDER_CLAUDE_MAIN_TARGET = "https://api.anthropic.com/v1"
73
+ $env:MOLENKOPF_PROVIDER_CLAUDE_MAIN_CREDENTIAL_ENV = "ANTHROPIC_MAIN_API_KEY"
74
+ $env:MOLENKOPF_PROVIDER_CLAUDE_MAIN_AUTH = "x-api-key"
75
+ $env:ANTHROPIC_MAIN_API_KEY = "replace-with-anthropic-main-api-key"
76
+
77
+ $env:MOLENKOPF_PROVIDER_CLAUDE_WORK_NAME = "Claude Work"
78
+ $env:MOLENKOPF_PROVIDER_CLAUDE_WORK_TARGET = "https://api.anthropic.com/v1"
79
+ $env:MOLENKOPF_PROVIDER_CLAUDE_WORK_CREDENTIAL_ENV = "ANTHROPIC_WORK_API_KEY"
80
+ $env:MOLENKOPF_PROVIDER_CLAUDE_WORK_AUTH = "x-api-key"
81
+ $env:ANTHROPIC_WORK_API_KEY = "replace-with-anthropic-work-api-key"
82
+
83
+ npm run dev
84
+ ```
85
+
86
+ ## Private Env File
87
+
88
+ `.env` is ignored by git and loaded automatically by source runs. It may contain
89
+ the required session secret and provider environment variables. Replace the
90
+ placeholder session secret before starting Molenkopf:
91
+
92
+ ```env
93
+ MOLENKOPF_SESSION_SECRET=replace-with-at-least-32-random-characters
94
+ MOLENKOPF_PROVIDER_IDS=openai-main,claude-main
95
+ MOLENKOPF_PROVIDER_OPENAI_MAIN_NAME=OpenAI Main
96
+ MOLENKOPF_PROVIDER_OPENAI_MAIN_TARGET=https://api.openai.com/v1
97
+ MOLENKOPF_PROVIDER_OPENAI_MAIN_CREDENTIAL_ENV=OPENAI_MAIN_API_KEY
98
+ OPENAI_MAIN_API_KEY=replace-with-openai-main-api-key
99
+ MOLENKOPF_PROVIDER_CLAUDE_MAIN_NAME=Claude Main
100
+ MOLENKOPF_PROVIDER_CLAUDE_MAIN_TARGET=https://api.anthropic.com/v1
101
+ MOLENKOPF_PROVIDER_CLAUDE_MAIN_CREDENTIAL_ENV=ANTHROPIC_MAIN_API_KEY
102
+ MOLENKOPF_PROVIDER_CLAUDE_MAIN_AUTH=x-api-key
103
+ ANTHROPIC_MAIN_API_KEY=replace-with-anthropic-main-api-key
104
+ ```
105
+
106
+ `--env-file FILE` remains available when you intentionally want a different
107
+ private file. Docker does not automatically read host `.env` files; pass the
108
+ file explicitly with `docker run --env-file .env ...`.
109
+
110
+ ## Fields
111
+
112
+ ```text
113
+ MOLENKOPF_PROVIDER_<ID>_NAME
114
+ MOLENKOPF_PROVIDER_<ID>_TARGET
115
+ MOLENKOPF_PROVIDER_<ID>_CREDENTIAL_ENV
116
+ MOLENKOPF_PROVIDER_<ID>_AUTH=bearer | x-api-key | none
117
+ MOLENKOPF_PROVIDER_<ID>_KIND=api | local
118
+ MOLENKOPF_PROVIDER_<ID>_ENABLED=false
119
+ ```
120
+
121
+ When a configured provider is selected, Molenkopf strips incoming client auth
122
+ and injects the configured server-side env credential at the forwarding
123
+ boundary.
@@ -0,0 +1,195 @@
1
+ # Molenkopf Usage
2
+
3
+ This is the practical local test flow for Molenkopf.
4
+
5
+ ## Start
6
+
7
+ ```bash
8
+ cp .env.example .env
9
+ # Edit .env and set MOLENKOPF_SESSION_SECRET.
10
+ npm run dev
11
+ ```
12
+
13
+ Source runs load `./.env` automatically without overriding values already
14
+ exported in your shell.
15
+
16
+ Open `http://127.0.0.1:8787/__molenkopf/dashboard`. Use
17
+ `http://127.0.0.1:8787/v1` as the OpenAI-compatible base URL in local clients.
18
+
19
+ ## Authenticate Proxy Traffic
20
+
21
+ Create a Molenkopf API key in the dashboard after first-run setup. Every
22
+ `/v1/...` proxy request needs that key. Use `Authorization: Bearer mk_...` when
23
+ Molenkopf supplies provider credentials. If your client also needs to forward an
24
+ upstream `Authorization` header, send the Molenkopf key as `x-molenkopf-token`.
25
+
26
+ ```text
27
+ Authorization: Bearer mk_...
28
+ x-molenkopf-token: mk_...
29
+ ```
30
+
31
+ `x-molenkopf-agent` is optional local request metadata. It can select only an
32
+ agent/provider binding already allowed by the authenticated key, team, and
33
+ profile policy. Molenkopf strips local Molenkopf auth and routing headers before
34
+ forwarding upstream.
35
+
36
+ ## Test Request
37
+
38
+ Send a normal OpenAI-compatible request through the proxy. Then refresh the dashboard.
39
+
40
+ ```bash
41
+ MOLENKOPF_API_KEY="mk_..."
42
+ curl http://127.0.0.1:8787/v1/responses \
43
+ -H "x-molenkopf-token: ${MOLENKOPF_API_KEY}" \
44
+ -H "authorization: Bearer ${OPENAI_API_KEY}" \
45
+ -H "content-type: application/json" \
46
+ -d '{"model":"gpt-4.1-mini","input":"short local test"}'
47
+ ```
48
+
49
+ PowerShell:
50
+
51
+ ```powershell
52
+ $env:MOLENKOPF_API_KEY = "mk_..."
53
+ curl.exe http://127.0.0.1:8787/v1/responses `
54
+ -H "x-molenkopf-token: $env:MOLENKOPF_API_KEY" `
55
+ -H "authorization: Bearer $env:OPENAI_API_KEY" `
56
+ -H "content-type: application/json" `
57
+ -d '{ "model": "gpt-4.1-mini", "input": "short local test" }'
58
+ ```
59
+
60
+ Expected result:
61
+
62
+ - `/__molenkopf/requests/latest` returns a redacted audit manifest for an
63
+ authenticated admin session.
64
+ - `Dashboard -> Overview` updates after refresh or polling.
65
+ - `Context compression flow` increments request and token counters.
66
+ - `Memory graph workspace` shows text-derived safe graph data after transferred text is observed.
67
+
68
+ ## Provider Setup
69
+
70
+ `molenkopf.config.json` is the provider startup source when present or passed
71
+ with `--config`. Provider API keys must be referenced from environment
72
+ variables with `auth.credentialRef`, for example `env:OPENAI_MAIN_API_KEY`.
73
+ Inline credentials are rejected in file config. The setup for ENV-defined
74
+ providers remains in
75
+ [MOLENKOPF_PROVIDER_ENV.md](MOLENKOPF_PROVIDER_ENV.md).
76
+
77
+ Start from the example:
78
+
79
+ ```powershell
80
+ Copy-Item molenkopf.config.example.json molenkopf.config.json
81
+ $env:OPENAI_MAIN_API_KEY = "<your OpenAI API key>"
82
+ $env:ANTHROPIC_MAIN_API_KEY = "<your Anthropic API key>"
83
+ molenkopf proxy --config molenkopf.config.json
84
+ ```
85
+
86
+ Then inspect:
87
+
88
+ ```text
89
+ http://127.0.0.1:8787/__molenkopf/providers
90
+ http://127.0.0.1:8787/__molenkopf/config
91
+ ```
92
+
93
+ Expected:
94
+
95
+ - only the JSON providers are listed
96
+ - `credentialRef: env:OPENAI_MAIN_API_KEY` and `credentialConfigured: true`
97
+ are visible after the environment variable is set
98
+ - real API key values are loaded from the local environment but not returned by
99
+ Local API
100
+ - no ENV provider blocks are mixed into JSON startup
101
+
102
+ ### Local CLI And Local Models
103
+
104
+ Use `kind: "cli-claude"` or `kind: "cli-codex"` for local CLI accounts, and
105
+ `kind: "ollama"` or `kind: "lmstudio"` for local OpenAI-compatible model
106
+ servers. Runtime-auth imports run CLI providers with isolated local auth/profile
107
+ directories. Provider switching is visible in Admin; explicit agent bindings can
108
+ route `x-molenkopf-agent` values to configured providers without bypassing
109
+ team/provider allowlists or API-key scopes.
110
+
111
+ Providers added through the Admin form are runtime state unless represented by
112
+ JSON config, env configuration, or imported runtime-auth metadata.
113
+
114
+ ## Users, Teams, Projects, And Keys
115
+
116
+ Molenkopf separates project attribution from team policy:
117
+
118
+ - `Project` is required on every API key. It describes the workload that uses
119
+ the key, for example `project-a`, `billing-batch`, or `local-test`.
120
+ - `Team` is the organizational policy bucket. Teams own provider allowlists,
121
+ budget accounting, and scoped visibility. First-run setup creates the default
122
+ `everyone` team for the first admin.
123
+ - `User` is the login and key owner. Users can belong to one or more teams.
124
+
125
+ Dashboard support: Admin manages users, teams, providers, and system controls;
126
+ Overview lets the signed-in user create and revoke permitted project keys. Raw
127
+ key secrets are shown once only.
128
+
129
+ Local API support:
130
+
131
+ | Action | Endpoint |
132
+ | --- | --- |
133
+ | List users and teams | `GET /__molenkopf/identity` |
134
+ | Create/update user | `POST /__molenkopf/identity/users` |
135
+ | Remove user | `POST /__molenkopf/identity/users/remove` |
136
+ | Create/update team | `POST /__molenkopf/identity/teams` |
137
+ | Remove team | `POST /__molenkopf/identity/teams/remove` |
138
+ | Create/list/revoke keys | `/__molenkopf/keys` and `/__molenkopf/keys/revoke` |
139
+
140
+ If a user belongs to multiple teams, key creation must choose the team. If a
141
+ user belongs to one team, Molenkopf can use that team automatically.
142
+
143
+ ## Troubleshooting
144
+
145
+ - Dashboard stuck on `connecting`: restart the proxy so the current dashboard bundle is served.
146
+ - `EADDRINUSE`: another proxy is already listening on the port; stop that process or start on another port.
147
+ - `invalid_api_key`: create a Molenkopf project key in the dashboard and send it
148
+ as `Authorization: Bearer mk_...` or `x-molenkopf-token: mk_...`.
149
+ - `OPENAI_API_KEY missing`: set the provider environment variable or forward an
150
+ upstream `Authorization` header with `x-molenkopf-token` for Molenkopf auth.
151
+ - No graph nodes: send API traffic through `/v1/...`; internal dashboard requests are intentionally ignored.
152
+ - Saved tokens stay `0`: the payload may be small or the compressor is disabled.
153
+ - Imported Claude auth works but write/edit prompts still appear: that is the outer Claude harness permission profile, not the imported runtime auth. Track and fix this separately from provider auth.
154
+ - Claude/Codex no-response: use the provider card `Test` action first. Molenkopf reports spawned CLI lifecycle, timeout, malformed output, and permission-prompt classes for the child process it owns.
155
+
156
+ ## Current Dashboard Views
157
+
158
+ - `Overview`: connection status, signed-in user usage, teams, members, project
159
+ keys, and client connection snippets.
160
+ - `Admin`: users, teams, provider accounts, routing, plugin controls, imported runtime auth, and workspace links.
161
+
162
+ Dedicated Providers, Plugins, Requests, Audit, Agents, and Settings views are
163
+ planned in `ROADMAP.md`. Until then, use the local APIs and plugin pages below
164
+ for request/audit/plugin details.
165
+
166
+ ## Plugin Pages And Data
167
+
168
+ Open plugin pages from the Admin plugin section. Context compression owns token
169
+ pressure and savings views. The graph page is fed from redacted transferred text
170
+ and safe request metadata; it does not read an Obsidian vault yet and does not
171
+ render raw prompt or response content.
172
+
173
+ ## Current Boundaries
174
+
175
+ - No full prompts or full responses are displayed.
176
+ - File config rejects inline raw API credentials; use `auth.credentialRef`.
177
+ Provider credentials entered in the dashboard and imported runtime auth are
178
+ intentional local operator state, and Local API responses do not display
179
+ credential values.
180
+ - Token hash drafts store hash metadata only; list responses show only that a hash exists plus a short fingerprint.
181
+ - Provider routing is explicit: teams carry provider allowlists, API keys carry
182
+ project attribution, and provider selection can be manual or distributed by
183
+ configured profile.
184
+ - Plugin toggle and order state persists in local runtime settings. Locked
185
+ plugin settings are normalized back to safe defaults on startup.
186
+
187
+ ## Verification
188
+
189
+ Run:
190
+
191
+ ```bash
192
+ npm test
193
+ ```
194
+
195
+ The relevant checks cover dashboard rendering, script parsing, provider/plugin controls, agent draft safety, audit summaries, plugin pages, and proxy behavior.
@@ -0,0 +1,36 @@
1
+ # Molenkopf Product Intent
2
+
3
+ Molenkopf is a local agent gateway for API and CLI based coding agents. The
4
+ product must connect OpenAI/Codex and Anthropic/Claude traffic into one local
5
+ endpoint and one local memory layer.
6
+
7
+ ## Target
8
+
9
+ - Accept agent traffic from OpenAI-compatible APIs, Anthropic/Claude-compatible APIs, Codex CLI, and Claude CLI.
10
+ - Let CLI agents run locally while their prompt/context stream is still observable through the gateway.
11
+ - Reduce the real text/context that is sent upstream or into a CLI runtime.
12
+ - Show live token savings only from real transferred payloads, not placeholder counters.
13
+ - Store safe derived memory from transferred text so later turns can reuse it.
14
+ - Build an Obsidian-style graph from the transferred text and derived memory, not from generic HTTP metadata.
15
+
16
+ ## Plugin Meaning
17
+
18
+ Plugins are local capabilities in the agent path, not decorative dashboard pages.
19
+ Plugins are middleware, but not all middleware can mutate traffic. Mutation
20
+ rights are explicit plugin descriptor fields and are enforced by the proxy.
21
+
22
+ - Context compression plugin: owns token reduction, before/after accounting, saved-token totals, skip reasons, and retrieval IDs for real context chunks.
23
+ - Memory/Obsidian plugin: owns the derived text memory graph, semantic nodes, links, and local store updates from real agent text.
24
+ - Provider/runtime plugins: adapt OpenAI API, Anthropic API, Codex CLI, Claude CLI, and later MCP into the same run/event contract.
25
+ - Core safety pipeline: redacts secrets and prevents raw credentials, full prompts, and full responses from leaking into UI/logs. This is not optional plugin behavior.
26
+
27
+ ## UI Rules
28
+
29
+ - Empty state must say no payloads observed yet. It must not show fake token pressure.
30
+ - No unauthenticated proxy traffic graph as a product target. If attribution is
31
+ unknown, show it only as a routing/accounting warning.
32
+ - Compression views must answer: what text entered, what was removed or represented by ID, what was sent, and how many tokens were saved.
33
+ - Graph views must answer: what concepts/entities/threads were learned from transferred text and how they relate.
34
+ - HTTP metadata graphs are diagnostics only, not the Obsidian workspace goal.
35
+
36
+ Molenkopf should adapt that pattern at the gateway/runtime boundary instead of pretending all agents are API-key-only HTTP clients.
@@ -0,0 +1,94 @@
1
+ # Molenkopf Threat Model
2
+
3
+ Date: 2026-06-23
4
+
5
+ This model describes the public Molenkopf product target and calls out
6
+ unfinished hardening explicitly.
7
+
8
+ ## Assets
9
+
10
+ - Upstream provider credentials.
11
+ - Molenkopf employee and agent tokens.
12
+ - Raw prompts and raw responses.
13
+ - Retrieval originals and derived summaries.
14
+ - Audit manifests and token accounting.
15
+ - Provider selection, plugin toggles, and memory export controls.
16
+
17
+ ## Attackers
18
+
19
+ - Local untrusted process on the same machine.
20
+ - Malicious local webpage that can reach loopback endpoints.
21
+ - Another employee on the LAN if public bind is enabled.
22
+ - Compromised plugin or dashboard payload.
23
+ - Over-privileged internal agent token.
24
+ - Accidental operator mistake, such as sending secrets in query strings.
25
+
26
+ ## Boundaries
27
+
28
+ ```text
29
+ employee client
30
+ -> Molenkopf auth boundary
31
+ -> policy and plugin boundary
32
+ -> provider credential boundary
33
+ -> upstream provider
34
+ ```
35
+
36
+ ```text
37
+ safe transferred text
38
+ -> redaction
39
+ -> compression
40
+ -> retrieval or memory store
41
+ -> audit/dashboard/exports
42
+ ```
43
+
44
+ ## Hardened Release Invariants
45
+
46
+ These are target invariants for the team gateway. Remaining hardening work is
47
+ tracked through the roadmap, GitHub issues, and release PRs.
48
+
49
+ - No provider credential values in audit, dashboard, events, docs, or plugin data.
50
+ - No full prompt or full response in audit, dashboard, events, or plugin pages.
51
+ - Query strings are stripped or redacted before audit persistence.
52
+ - First-run open mode exposes only health, current-session status, and
53
+ first-run admin creation.
54
+ - `__molenkopf/*` control APIs require auth after setup; provider, plugin,
55
+ routing, agent, stats, event, config metadata, and retention endpoints are
56
+ admin-only.
57
+ - `/v1/*` proxy traffic requires a valid Molenkopf API key.
58
+ - Non-loopback source binds require an explicit `--allow-public-bind` opt-in.
59
+ - Molenkopf tokens are stored as hashes and shown once.
60
+ - Retrieval writes happen only after a real compression artifact is committed.
61
+ - Obsidian writes require dry-run and path guards before apply.
62
+ - Multi-account routing uses explicit provider profiles only.
63
+
64
+ ## Implemented Plugin And Core Safety Invariants
65
+
66
+ - Plugins are optional extensions; core safety, audit, event, storage, and
67
+ routing code is not exposed as a plugin and is not user-toggleable.
68
+ - Plugin descriptors declare type, read scopes, mutation scopes, toggle policy,
69
+ and workspace data scopes.
70
+ - The request pipeline restores unauthorized body, route, and block mutations
71
+ and fails closed with `plugin_capability_violation`.
72
+ - Remote plugin loading is disabled.
73
+ - Core redaction runs before optional plugin middleware.
74
+
75
+ ## Storage Policy
76
+
77
+ - Audit manifests are metadata-only. Manual purge exists; retention policy,
78
+ quotas, pagination, and project scope are planned.
79
+ - Retrieval originals are local sensitive artifacts. Manual purge exists;
80
+ retention policy, quotas, and project scope are planned.
81
+ - Memory currently stores a bounded in-memory derived graph. Persistent memory,
82
+ source refs, and retention policy are planned.
83
+ - Dashboard state must not persist raw tokens.
84
+
85
+ ## Required Tests
86
+
87
+ - Query secret does not appear in `/requests`, latest audit, summaries, SSE, dashboard, or plugin pages.
88
+ - Nested JSON secrets are redacted before compression and audit.
89
+ - `x-molenkopf-token` and local routing headers never reach upstream.
90
+ - Unauthenticated control APIs return `401`; insufficient scopes return `403`.
91
+ - Non-loopback bind without explicit opt-in fails at startup.
92
+ - Revoked/expired tokens cannot call `/v1/*` or control APIs.
93
+ - Retrieval no-op compression leaves no stored original.
94
+ - Obsidian apply cannot write outside the selected vault root.
@@ -0,0 +1,68 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "server": {
4
+ "bindHost": "127.0.0.1",
5
+ "port": 8787,
6
+ "dataDir": ".molenkopf"
7
+ },
8
+ "providers": [
9
+ {
10
+ "id": "openai-main",
11
+ "name": "OpenAI Main",
12
+ "kind": "openai-compatible",
13
+ "baseUrl": "https://api.openai.com/v1",
14
+ "auth": {
15
+ "scheme": "bearer",
16
+ "credentialRef": "env:OPENAI_MAIN_API_KEY"
17
+ },
18
+ "enabled": true
19
+ },
20
+ {
21
+ "id": "claude-main",
22
+ "name": "Claude Main",
23
+ "kind": "anthropic",
24
+ "baseUrl": "https://api.anthropic.com/v1",
25
+ "auth": {
26
+ "scheme": "x-api-key",
27
+ "credentialRef": "env:ANTHROPIC_MAIN_API_KEY"
28
+ },
29
+ "enabled": true
30
+ },
31
+ {
32
+ "id": "claude-local",
33
+ "name": "Claude Local CLI",
34
+ "kind": "cli-claude",
35
+ "command": "claude",
36
+ "args": ["--print"],
37
+ "inputMode": "stdin",
38
+ "timeoutMs": 120000,
39
+ "enabled": false
40
+ }
41
+ ],
42
+ "profiles": [
43
+ {
44
+ "id": "default-local",
45
+ "providerId": "openai-main",
46
+ "allowedModels": ["gpt-4.1-mini"],
47
+ "defaultModel": "gpt-4.1-mini"
48
+ }
49
+ ],
50
+ "pluginPolicies": [
51
+ {
52
+ "id": "standard-policy",
53
+ "enabledPluginIds": ["context-compressor-plugin"],
54
+ "remotePlugins": false
55
+ }
56
+ ],
57
+ "agents": [
58
+ {
59
+ "id": "local-codex",
60
+ "ownerId": "local",
61
+ "kind": "local-agent",
62
+ "profileId": "default-local",
63
+ "pluginPolicyId": "standard-policy",
64
+ "scopes": ["proxy:use"],
65
+ "enabled": true
66
+ }
67
+ ]
68
+ }
package/package.json ADDED
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "@bothat-io/molenkopf",
3
+ "version": "0.1.2",
4
+ "description": "Local gateway for API and CLI based coding agents with routing, redaction, compression, memory, audit, and dashboard controls.",
5
+ "author": "Molenkopf contributors",
6
+ "license": "MIT",
7
+ "private": false,
8
+ "type": "module",
9
+ "bin": {
10
+ "molenkopf": "bin/molenkopf.js"
11
+ },
12
+ "engines": {
13
+ "node": ">=24"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/bothat-io/molenkopf.git"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/bothat-io/molenkopf/issues"
21
+ },
22
+ "homepage": "https://github.com/bothat-io/molenkopf#readme",
23
+ "keywords": [
24
+ "molenkopf",
25
+ "local-ai-gateway",
26
+ "coding-agents",
27
+ "proxy",
28
+ "redaction",
29
+ "audit"
30
+ ],
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "files": [
35
+ ".env.example",
36
+ "bin/",
37
+ "packages/core/src/",
38
+ "packages/proxy/src/",
39
+ "packages/plugins/context-compressor-plugin/descriptor.ts",
40
+ "packages/plugins/context-compressor-plugin/plugin.ts",
41
+ "packages/plugins/context-compressor-plugin/page.html",
42
+ "packages/plugins/obsidian-graph-plugin/descriptor.ts",
43
+ "packages/plugins/obsidian-graph-plugin/plugin.ts",
44
+ "packages/plugins/obsidian-graph-plugin/page.html",
45
+ "packages/plugins/shared/audit-projects.ts",
46
+ "packages/dashboard/dist/",
47
+ "packages/dashboard/public/molenkopf-logo.png",
48
+ "packages/dashboard/public/favicon.png",
49
+ "docs/DEPLOYMENT.md",
50
+ "docs/MOLENKOPF_PLUGIN_API.md",
51
+ "docs/MOLENKOPF_PROVIDER_ENV.md",
52
+ "docs/MOLENKOPF_USAGE.md",
53
+ "docs/PRODUCT_INTENT.md",
54
+ "docs/THREAT_MODEL.md",
55
+ "molenkopf.config.example.json",
56
+ "README.md",
57
+ "LICENSE",
58
+ "SECURITY.md"
59
+ ],
60
+ "scripts": {
61
+ "bootstrap": "npm ci && npm --prefix packages/dashboard ci",
62
+ "typecheck": "npm run typecheck:node && npm --prefix packages/dashboard run typecheck",
63
+ "typecheck:node": "tsc -p tsconfig.node.json",
64
+ "test": "npm run test:unit",
65
+ "test:unit": "npm run check:docs && npm run check:dashboard-install && npm run check:reachability && npm run check:source-completeness && npm run check:container-contract && npm run check:line-limits && npm run typecheck && npm --prefix packages/dashboard run build && npm run check:package && npm run test:scripts && npm run test:launcher && npm run vitest && npm run test:node",
66
+ "check:docs": "node scripts/check-doc-paths.js",
67
+ "check:dashboard-install": "node scripts/check-dashboard-install.js",
68
+ "check:reachability": "node scripts/check-reachability.js",
69
+ "check:source-completeness": "node scripts/check-source-completeness.js",
70
+ "check:container-contract": "node scripts/check-container-contract.js",
71
+ "check:package": "node scripts/check-package-contents.js",
72
+ "check:line-limits": "node scripts/check-line-limits.js",
73
+ "test:scripts": "node --test scripts/*.test.js",
74
+ "test:launcher": "node --test test/*.test.js",
75
+ "test:node": "node --experimental-strip-types --experimental-sqlite --disable-warning=ExperimentalWarning --import ./packages/proxy/test/setup.ts --test --test-concurrency=1 packages/core/test/*.test.ts packages/proxy/test/*.test.ts packages/dashboard/test/*.test.ts",
76
+ "test:all": "npm run test:unit && npm run e2e",
77
+ "vitest": "npm --prefix packages/dashboard run test",
78
+ "e2e": "npm --prefix packages/dashboard run e2e",
79
+ "prepack": "npm --prefix packages/dashboard run build && npm run check:package",
80
+ "pack:dry": "npm pack --dry-run",
81
+ "smoke:installed": "node scripts/smoke-installed-package.js",
82
+ "smoke:docker": "node scripts/smoke-docker.js",
83
+ "release:npm:check": "node scripts/check-npm-release.js",
84
+ "release:verify": "npm run test:unit && npm run e2e && npm run smoke:installed && npm run smoke:docker",
85
+ "serve:dev": "node --experimental-strip-types --experimental-sqlite --disable-warning=ExperimentalWarning packages/proxy/src/cli/profile-server.ts dev",
86
+ "serve:test": "node --experimental-strip-types --experimental-sqlite --disable-warning=ExperimentalWarning packages/proxy/src/cli/profile-server.ts test",
87
+ "serve:prod": "npm --prefix packages/dashboard run build && node --experimental-strip-types --experimental-sqlite --disable-warning=ExperimentalWarning packages/proxy/src/cli/profile-server.ts prod",
88
+ "dev": "npm run serve:dev",
89
+ "prod": "npm run serve:prod"
90
+ },
91
+ "directories": {
92
+ "doc": "docs"
93
+ },
94
+ "devDependencies": {
95
+ "@types/node": "^24.0.0",
96
+ "typescript": "^5.9.0"
97
+ }
98
+ }
@@ -0,0 +1,47 @@
1
+ import { randomBytes, scrypt, scryptSync, timingSafeEqual } from "node:crypto";
2
+ import { promisify } from "node:util";
3
+
4
+ // Password hashing with scrypt (Node built-in). We store salt + hash only, never
5
+ // the raw password. Used for admin/user login.
6
+
7
+ const scryptAsync = promisify(scrypt);
8
+ export const MAX_PASSWORD_BYTES = 4096;
9
+ export type PasswordHash = { salt: string; hash: string; version?: 1; keyLength?: number };
10
+
11
+ export function hashPassword(password: string): PasswordHash {
12
+ assertPasswordSize(password);
13
+ const salt = randomBytes(16).toString("hex");
14
+ const hash = scryptSync(password, salt, 64).toString("hex");
15
+ return { salt, hash, version: 1, keyLength: 64 };
16
+ }
17
+
18
+ export async function hashPasswordAsync(password: string): Promise<PasswordHash> {
19
+ assertPasswordSize(password);
20
+ const salt = randomBytes(16).toString("hex");
21
+ const keyLength = 64;
22
+ const hash = (await scryptAsync(password, salt, keyLength) as Buffer).toString("hex");
23
+ return { salt, hash, version: 1, keyLength };
24
+ }
25
+
26
+ export function verifyPassword(password: string, stored: PasswordHash | undefined): boolean {
27
+ if (!stored?.salt || !stored.hash) return false;
28
+ if (passwordTooLong(password)) return false;
29
+ const candidate = scryptSync(password, stored.salt, stored.keyLength ?? 64);
30
+ const expected = Buffer.from(stored.hash, "hex");
31
+ return candidate.length === expected.length && timingSafeEqual(candidate, expected);
32
+ }
33
+
34
+ export async function verifyPasswordAsync(password: string, stored: PasswordHash | undefined): Promise<boolean> {
35
+ if (!stored?.salt || !stored.hash || passwordTooLong(password)) return false;
36
+ const candidate = await scryptAsync(password, stored.salt, stored.keyLength ?? 64) as Buffer;
37
+ const expected = Buffer.from(stored.hash, "hex");
38
+ return candidate.length === expected.length && timingSafeEqual(candidate, expected);
39
+ }
40
+
41
+ export function passwordTooLong(password: string): boolean {
42
+ return Buffer.byteLength(password, "utf8") > MAX_PASSWORD_BYTES;
43
+ }
44
+
45
+ function assertPasswordSize(password: string): void {
46
+ if (passwordTooLong(password)) throw new Error("password_too_long");
47
+ }