@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
package/.env.example ADDED
@@ -0,0 +1,2 @@
1
+ # Required for stable signed browser sessions. Change before starting Molenkopf.
2
+ MOLENKOPF_SESSION_SECRET=replace-with-at-least-32-random-characters
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 bothat.io and Molenkopf contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,199 @@
1
+ <p align="center">
2
+ <img src="packages/dashboard/public/molenkopf-logo.png" alt="Molenkopf logo" width="72">
3
+ </p>
4
+
5
+ # Molenkopf
6
+
7
+ Molenkopf is a local gateway for API and CLI based coding agents. It is not
8
+ only an OpenAI-compatible API proxy: it also supports Anthropic/Claude API
9
+ traffic and local CLI runtimes such as Claude CLI and Codex CLI. It should
10
+ reduce real transferred context, redact secrets, build local derived memory,
11
+ and write audit manifests without full prompts or responses.
12
+
13
+ Molenkopf has a fixed core safety pipeline for secret redaction, content classification, safe compression for logs, JSON and stacktraces, local retrieval storage, audit manifests, and redacted SSE events. Optional plugins extend this pipeline; core safety behavior is not toggleable.
14
+
15
+ Product intent and non-negotiable plugin semantics live in
16
+ `docs/PRODUCT_INTENT.md` and `docs/MOLENKOPF_PLUGIN_API.md`.
17
+
18
+ ## Safety Notice
19
+
20
+ Do not run Molenkopf blindly with real provider accounts, private repositories,
21
+ browser sessions, or imported runtime credentials. Review `SECURITY.md` first,
22
+ keep source runs on the default loopback bind, and treat `.molenkopf/`, audit
23
+ files, retrieval stores, databases, env files, and runtime-auth profiles as
24
+ sensitive local state.
25
+
26
+ ## Implemented Baseline
27
+
28
+ Built now:
29
+
30
+ - Local HTTP proxy bound to `127.0.0.1` by default.
31
+ - Transparent streaming gateway for OpenAI- and Anthropic-compatible traffic via
32
+ base-URL interception (`OPENAI_BASE_URL` / `ANTHROPIC_BASE_URL`). Responses
33
+ are streamed through byte-for-byte where possible (SSE, gzip, headers
34
+ preserved); the agent runs normally and its context stream flows through
35
+ Molenkopf.
36
+ - Transparent by default: the request body is never altered unless a transform plugin is explicitly enabled. Compression is opt-in and only reduces structured/operational content (json/log/stacktrace/shell); prose, markdown, source code, and diffs pass through untouched.
37
+ - Per-agent multi-account routing: each agent (`x-molenkopf-agent`) routes to its bound provider/account; config `profiles`/`agents` resolve to providers.
38
+ - Real token accounting: upstream `usage` is read from the provider response and recorded as
39
+ real input/output tokens (not only a chars/4 estimate).
40
+ - Text-derived memory graph: concepts (files, symbols, error types) are extracted from the
41
+ real redacted transferred text into a bounded co-occurrence graph.
42
+ - Static pipeline with request IDs, redaction, classification, compression, retrieval, audit, SSE events, and upstream routing.
43
+ - Local API under `/__molenkopf/*`: bootstrap endpoints for health, session
44
+ status, and first-run admin creation, user-scoped usage/key endpoints, and admin-only
45
+ provider/plugin/agent/stats/events/config metadata plus retention purge.
46
+ - Dashboard shell served at `/__molenkopf/dashboard` with Overview and Admin
47
+ views backed by an isolated React/Vite dashboard package. Dedicated Providers,
48
+ Plugins, Requests, Audit, Agents, and Settings views are planned.
49
+ - Plugin pages are local HTML surfaces under `/__molenkopf/plugins/:id/page`; context compression and memory graph pages read scoped plugin data endpoints and show explicit load errors instead of fake empty workspaces.
50
+ - The context compression plugin must expose safe token accounting for real transferred context only. Empty states must not show placeholder pressure or fake savings. Usage notes: `docs/CONTEXT_COMPRESSION_PLUGIN_README.md`.
51
+ - Practical dashboard and proxy connection guide: `docs/MOLENKOPF_USAGE.md`.
52
+ - JSON config startup target for providers, agents, and plugin policies:
53
+ `docs/MOLENKOPF_JSON_CONFIG_PLAN.md`.
54
+ - Multi-provider env setup: `docs/MOLENKOPF_PROVIDER_ENV.md`.
55
+ - Root `/` redirects to the local dashboard; upstream API traffic stays on `/v1/...`.
56
+ - Claude/Anthropic-compatible target resolution through `ANTHROPIC_BASE_URL`; Molenkopf-owned CLI runtime bridging exists for local `claude` and `codex` child processes.
57
+ - Profile routing primitives for fixed, manual, and failover routing with env credentials, budgets, and health summaries.
58
+ - Local plugin SDK with explicit permissions and remote plugin loading disabled.
59
+ - CI mode primitives for PR context packing and audit artifacts.
60
+
61
+ Explicitly not connected:
62
+
63
+ - Remote issue-tracker integration.
64
+ - Remote plugin installation.
65
+ - Credential-file scanning or config-file credential storage. Runtime auth import
66
+ stores credentials only when the operator explicitly imports a local profile.
67
+
68
+ ## Security Model
69
+
70
+ Core and proxy use Node.js built-ins only. Molenkopf API keys are stored as hashes, imported runtime auth is kept in isolated local profile directories, and Local API responses hide provider credentials. Proxy traffic on `/v1/...` requires a valid Molenkopf API key. The default upstream route can forward incoming auth headers only when the selected profile requires it; configured provider profiles strip incoming client auth and inject the server-side credential at the forwarding boundary. Prompts and responses are not logged in full. Source code and diffs pass through in safe mode.
71
+
72
+ Before the first admin exists, only health, session status, and browser first-run
73
+ admin creation are usable. After setup, Local API metadata is role-gated: normal
74
+ users get scoped usage and key data, while provider, plugin, agent, routing,
75
+ stats, events, and retention endpoints are admin-only.
76
+
77
+ Use `--allow-public-bind` only when you intentionally want to bind outside
78
+ localhost. If Docker is published publicly before an admin exists, the first-run
79
+ screen is reachable there until the first admin is created.
80
+
81
+ ## Control Plane Usage
82
+
83
+ Open `http://127.0.0.1:8787/__molenkopf/dashboard` after starting Molenkopf.
84
+ Root `/` redirects there, while upstream agent traffic still uses `/v1/...`.
85
+
86
+ Every `/v1/...` proxy request must present a valid Molenkopf API key. Use
87
+ `Authorization: Bearer mk_...` when Molenkopf supplies provider credentials. If
88
+ the client must also forward an upstream `Authorization` header, put the
89
+ Molenkopf key in `x-molenkopf-token: mk_...`; Molenkopf strips that header
90
+ before forwarding upstream.
91
+
92
+ ```text
93
+ Authorization: Bearer mk_...
94
+ ```
95
+
96
+ `x-molenkopf-agent` is optional local request metadata. It may select an
97
+ explicit agent binding only when that binding is already allowed for the
98
+ authenticated key, team, and provider policy; otherwise the request fails
99
+ closed.
100
+
101
+ Manual provider switching happens in the Admin provider section or by posting `{ "id": "openai-env" }` to `/__molenkopf/providers/select`. Explicit provider profiles, API-key scopes, team allowlists, and routing mode are enforced before forwarding; manual selection remains the default route when no explicit profile is attached.
102
+
103
+ Multiple provider profiles can be declared with `molenkopf.config.json` or
104
+ `MOLENKOPF_PROVIDER_IDS`. JSON profiles must reference credentials with
105
+ `auth.credentialRef` such as `env:OPENAI_API_KEY`; inline credentials are
106
+ rejected in file config. Local API responses hide credential values and show
107
+ only `credentialRef` plus configured state. Selected configured profiles inject
108
+ their credential at the forwarding boundary.
109
+
110
+ Users, teams, projects, and Molenkopf API keys are managed in the Dashboard.
111
+ Overview is the signed-in user's status, usage, teams, members, and project-key
112
+ surface. Admin contains provider, plugin, user, team, and system controls.
113
+ Projects are required key/workload labels; teams carry provider policy and usage
114
+ scope. See `docs/MOLENKOPF_USAGE.md` for the workflow and Local API endpoints.
115
+
116
+ Plugin toggles happen in the Admin plugin section or by posting `{ "id": "context-compressor-plugin", "enabled": false }` to `/__molenkopf/plugins/toggle`. Plugins are optional extensions; core safety, storage, audit, event, and routing code is not exposed as plugins and cannot be disabled through plugin controls. Remote plugin loading is disabled.
117
+
118
+ Agent drafts are stored as local proxy metadata through `/__molenkopf/agents/draft`; raw token fields are rejected and only `tokenHash` is accepted. The dashboard keeps a `localStorage` fallback if the local API is unavailable. These drafts are routing metadata, not provider credentials.
119
+
120
+ For a step-by-step local setup and test flow, read `docs/MOLENKOPF_USAGE.md`.
121
+
122
+ Plugin pages open in standalone windows from `/__molenkopf/plugins/context-compressor-plugin/page` and `/__molenkopf/plugins/obsidian-graph-plugin/page`. The graph workspace is derived from safe request metadata and redacted transferred text. Context and graph pages group by project/key where available and surface plugin-data failures explicitly.
123
+
124
+ ## Commands
125
+ Install from npm with Node.js 24 or newer:
126
+
127
+ ```bash
128
+ npm install -g @bothat-io/molenkopf
129
+ node -e "require('node:fs').writeFileSync('.env','MOLENKOPF_SESSION_SECRET='+require('node:crypto').randomBytes(32).toString('hex')+'\n')"
130
+ molenkopf proxy
131
+ ```
132
+
133
+ Quick Docker start on the Docker host:
134
+
135
+ ```bash
136
+ cp .env.example .env
137
+ # Edit .env and set a unique MOLENKOPF_SESSION_SECRET.
138
+ docker pull ghcr.io/bothat-io/molenkopf:latest
139
+ docker run --rm \
140
+ --env-file .env \
141
+ -p 127.0.0.1:8787:8787 \
142
+ -v molenkopf-data:/data \
143
+ ghcr.io/bothat-io/molenkopf:latest
144
+ ```
145
+
146
+ Open `http://127.0.0.1:8787/` and create the first admin user. The Docker
147
+ quickstart binds Molenkopf to `127.0.0.1` on the host for local use; do not
148
+ publish the port publicly before admin setup and deployment security are done.
149
+ Docker requires `--env-file .env`; admin users are created only in the browser.
150
+
151
+ Use `docs/DEPLOYMENT.md` when you need a different port or non-loopback access.
152
+
153
+ For local development, use a source checkout:
154
+
155
+ ```bash
156
+ npm run bootstrap
157
+ cp .env.example .env
158
+ # Edit .env and set a unique MOLENKOPF_SESSION_SECRET.
159
+ npm run dev
160
+ ```
161
+
162
+ ## Connect A Client
163
+
164
+ Use Molenkopf as the OpenAI-compatible base URL:
165
+
166
+ ```text
167
+ http://127.0.0.1:8787/v1
168
+ ```
169
+
170
+ Authenticate proxy traffic with a Molenkopf API key:
171
+
172
+ ```text
173
+ Authorization: Bearer mk_...
174
+ ```
175
+
176
+ If your client also sends an upstream provider credential in `Authorization`,
177
+ send the Molenkopf key separately:
178
+
179
+ ```text
180
+ x-molenkopf-token: mk_...
181
+ x-molenkopf-agent: codex-local
182
+ ```
183
+
184
+ These local headers are stripped before upstream forwarding. See
185
+ `docs/MOLENKOPF_USAGE.md` for a concrete `curl` request, provider setup, and
186
+ dashboard checks.
187
+
188
+ ## Limitations
189
+
190
+ Provider token accounting uses upstream `usage` when present and falls back to bounded estimates for local compression pressure. JSON summaries are readable summaries, not guaranteed valid JSON. v0.1 does not retry provider failures automatically and request-side safe compression remains opt-in.
191
+
192
+ Be careful with secrets and private repositories. Molenkopf reduces accidental
193
+ exposure, but local retrieval stores still contain bounded redacted excerpts.
194
+ They do not support full-original recovery and should be treated as sensitive
195
+ local artifacts.
196
+
197
+ ## License
198
+
199
+ MIT License. Copyright (c) 2026 bothat.io and Molenkopf contributors.
package/SECURITY.md ADDED
@@ -0,0 +1,36 @@
1
+ # Security
2
+
3
+ Molenkopf is a local gateway for coding-agent traffic. Do not run it blindly on
4
+ a workstation with real provider accounts, private repositories, browser
5
+ sessions, or imported runtime credentials. Review the configuration and runtime
6
+ flags first.
7
+
8
+ ## Safe Use
9
+
10
+ - Start on loopback only unless you intentionally need network access.
11
+ - Set a unique `MOLENKOPF_SESSION_SECRET` before starting Molenkopf. Do not
12
+ commit `.env` files or bake secrets into Docker images.
13
+ - Do not use `--allow-public-bind` unless you intentionally need network access.
14
+ - First-run bootstrap is intentionally narrow. Before an admin exists, only
15
+ health, session status, and first-run admin creation are usable.
16
+ - After setup, provider, plugin, routing, agent, stats, event, config metadata,
17
+ and retention purge endpoints are admin-only. Normal users receive scoped
18
+ usage and key data.
19
+ - Treat `.molenkopf/`, audit manifests, retrieval stores, SQLite
20
+ files, runtime-auth profiles, and env files as sensitive local state.
21
+ - Do not commit provider keys, imported `auth.json`, Claude credentials,
22
+ cookies, database files, audit files, retrieval stores, or screenshots with
23
+ secrets.
24
+ - Prefer environment credential references or a private local config file over
25
+ UI-entered provider credentials. File config rejects inline raw credentials.
26
+ - Read Docker and deployment settings before exposing a container port.
27
+
28
+ ## Reports
29
+
30
+ When the repository is hosted on GitHub, prefer GitHub private vulnerability
31
+ reporting if it is enabled. If only public issues are available, post a
32
+ sanitized reproduction only and keep sensitive material private.
33
+
34
+ Do not open a public issue that contains tokens, prompts, responses, auth files,
35
+ provider credentials, cookies, or private repository content. Redact the
36
+ material first and include only the minimal reproduction details.
@@ -0,0 +1,76 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { chmodSync, cpSync, existsSync, mkdtempSync, rmSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { dirname, join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const sourceRoot = dirname(dirname(fileURLToPath(import.meta.url)));
9
+ const signals = ["SIGINT", "SIGTERM"];
10
+
11
+ export function runLauncher(args = process.argv.slice(2), options = {}) {
12
+ const root = options.sourceRoot ?? sourceRoot;
13
+ const exit = options.exit ?? process.exit;
14
+ const signalSelf = options.signalSelf ?? ((name) => process.kill(process.pid, name));
15
+ const runtimeRoot = runtimeRootFor(root);
16
+ const entry = join(runtimeRoot, "packages", "proxy", "src", "cli", "main.ts");
17
+ if (!existsSync(entry)) throw new Error(`missing CLI entrypoint: ${entry}`);
18
+ const child = spawn(process.execPath, ["--experimental-strip-types", "--experimental-sqlite", "--disable-warning=ExperimentalWarning", entry, ...args], { stdio: options.stdio ?? "inherit" });
19
+ let closeRequested = false;
20
+ let finished = false;
21
+ let forced = false;
22
+ let requestedSignal = "";
23
+ let graceTimer;
24
+
25
+ const cleanup = () => cleanupRuntime(runtimeRoot, root);
26
+ const finish = (code, signal) => {
27
+ if (finished) return;
28
+ finished = true;
29
+ if (graceTimer) clearTimeout(graceTimer);
30
+ for (const name of signals) process.removeListener(name, handlers[name]);
31
+ cleanup();
32
+ const finalSignal = requestedSignal || signal;
33
+ if (finalSignal && process.platform !== "win32") signalSelf(finalSignal);
34
+ exit(code ?? (forced ? 1 : 1));
35
+ };
36
+ const handlers = Object.fromEntries(signals.map((name) => [name, () => {
37
+ if (closeRequested) return;
38
+ closeRequested = true;
39
+ requestedSignal = name;
40
+ child.kill(name);
41
+ graceTimer = setTimeout(() => {
42
+ forced = true;
43
+ child.kill("SIGKILL");
44
+ }, Number(process.env.MOLENKOPF_LAUNCHER_GRACE_MS ?? 5000)).unref();
45
+ }]));
46
+
47
+ child.on("close", finish);
48
+ child.on("error", (error) => {
49
+ console.error(error.message);
50
+ finish(1, null);
51
+ });
52
+ for (const name of signals) process.once(name, handlers[name]);
53
+ }
54
+
55
+ export function runtimeRootFor(root) {
56
+ if (!root.split(/[\\/]/).includes("node_modules")) return root;
57
+ const prefix = `molenkopf-cli-${createHash("sha256").update(root).digest("hex").slice(0, 12)}-`;
58
+ const runtimeRoot = mkdtempSync(join(tmpdir(), prefix));
59
+ try {
60
+ try { chmodSync(runtimeRoot, 0o700); } catch { /* Windows ACLs are inherited. */ }
61
+ for (const name of ["package.json", "packages", "bin"]) cpSync(join(root, name), join(runtimeRoot, name), { recursive: true });
62
+ return runtimeRoot;
63
+ } catch (error) {
64
+ cleanupRuntime(runtimeRoot, root);
65
+ throw error;
66
+ }
67
+ }
68
+
69
+ export function cleanupRuntime(path, root = sourceRoot) {
70
+ if (path === root) return;
71
+ try {
72
+ rmSync(path, { recursive: true, force: true, maxRetries: 10, retryDelay: 50 });
73
+ } catch (error) {
74
+ console.error(`cleanup warning: ${error instanceof Error ? error.message : String(error)}`);
75
+ }
76
+ }
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { runLauncher } from "./launcher.js";
3
+
4
+ runLauncher();
@@ -0,0 +1,104 @@
1
+ # Deployment
2
+
3
+ This document defines the current production deployment target for Molenkopf.
4
+ Routes, flags, and environment variables use Molenkopf names.
5
+
6
+ ## Local Modes
7
+
8
+ - Development: `npm run dev`
9
+ - Binds `127.0.0.1:8787`
10
+ - Uses `.molenkopf/dev`
11
+ - Starts the Vite dashboard unless `MOLENKOPF_DASHBOARD_DEV=0`
12
+ - Test server: `npm run serve:test`
13
+ - Binds `127.0.0.1:8798`
14
+ - Uses `.molenkopf/test`
15
+ - Local prod smoke: `npm run prod`
16
+ - Builds the dashboard
17
+ - Binds `127.0.0.1:8787`
18
+ - Uses `.molenkopf/prod`
19
+
20
+ `npm run prod` is a local production profile with durable local state. Override
21
+ it with `MOLENKOPF_PROD_PORT`, `MOLENKOPF_PROD_HOST`, `MOLENKOPF_PROD_TARGET`,
22
+ and `MOLENKOPF_PROD_DATA_DIR` when needed.
23
+
24
+ ## Docker Build
25
+
26
+ Build after the release source tree is clean:
27
+
28
+ ```powershell
29
+ docker build --pull -t molenkopf:local .
30
+ ```
31
+
32
+ The Docker image builds dashboard assets in a separate stage and copies only the
33
+ runtime source, plugin pages, root manifests, and built dashboard assets into the
34
+ final image. The final image also carries the MIT license notice. Core and Proxy
35
+ keep Node built-ins only.
36
+
37
+ ## Docker Run
38
+
39
+ The published image listens on `0.0.0.0:8787` inside the container so Docker port
40
+ publishing works. For local use, publish it only to host loopback:
41
+
42
+ ```powershell
43
+ Copy-Item .env.example .env
44
+ # Edit .env and set a unique MOLENKOPF_SESSION_SECRET.
45
+
46
+ docker run --rm `
47
+ --env-file .env `
48
+ -p 127.0.0.1:8787:8787 `
49
+ -v molenkopf-data:/data `
50
+ molenkopf:local
51
+ ```
52
+
53
+ Open `http://127.0.0.1:8787/` and create the first admin in the browser. Do not
54
+ seed admin usernames or passwords through environment variables. Docker starts
55
+ require a valid `MOLENKOPF_SESSION_SECRET`; copy `.env.example` to `.env`,
56
+ change the value, and pass it with `--env-file .env`. Docker does not
57
+ automatically read a host `.env` file. Configure provider credentials through
58
+ environment variables or a mounted config file that uses credential references.
59
+ Do not bake provider keys, imported runtime auth, `.env`, or local data into the
60
+ image.
61
+
62
+ ## Data Volume
63
+
64
+ Mount one durable volume at `/data`. It contains SQLite files, audit manifests,
65
+ retrieval store files, runtime settings, and imported runtime profiles. Treat the
66
+ volume as sensitive local state.
67
+
68
+ On POSIX filesystems Molenkopf creates and repairs sensitive state directories as
69
+ `0700` and sensitive files as `0600`, including SQLite, audit, retrieval, runtime
70
+ settings, and imported runtime auth/profile files. Windows does not expose full
71
+ POSIX permission bits through Node, so protect the data directory with Windows
72
+ ACLs or a private user profile.
73
+
74
+ Admins can manually purge audit and retrieval data with
75
+ `POST /__molenkopf/retention/purge` and an explicit `scope` of `audit`,
76
+ `retrieval`, or `all`. TTLs, quotas, pagination, and project-scoped retention
77
+ policies are still future work.
78
+
79
+ Current constraint: one writer only. Do not run multiple containers against the
80
+ same SQLite volume.
81
+
82
+ ## Security Gates
83
+
84
+ - Non-loopback source binds require `--allow-public-bind`.
85
+ - `MOLENKOPF_SESSION_SECRET` is required for every server start.
86
+ - `/v1/...` proxy APIs require a valid Molenkopf API key.
87
+ - `x-molenkopf-token` carries Molenkopf auth when a client must keep
88
+ `Authorization` for the upstream provider; it is stripped before forwarding.
89
+ - `/__molenkopf/health` is public.
90
+ - Before first admin setup, only health, session status, and first-run admin
91
+ creation are usable.
92
+ - Control-plane APIs require auth after an admin exists; provider, plugin,
93
+ routing, agent, stats, event, config metadata, and retention endpoints are
94
+ admin-only.
95
+ - Full prompts, full responses, provider credentials, cookies, auth headers, and
96
+ imported auth JSON must never appear in local API responses, plugin data, logs,
97
+ audit files, or docs.
98
+
99
+ ## Not Supported Yet
100
+
101
+ - Dockerized host Claude/Codex CLI runtimes by default.
102
+ - Multi-replica deployment with one SQLite volume.
103
+ - Remote plugin installation.
104
+ - Obsidian vault writes without dry-run and path guards.
@@ -0,0 +1,113 @@
1
+ # Molenkopf Plugin API
2
+
3
+ Molenkopf plugins are optional local extensions. Core safety, credential
4
+ handling, routing enforcement, audit invariants, and request redaction are core
5
+ responsibilities and are not exposed as plugins.
6
+
7
+ Each plugin is a TypeScript module at
8
+ `packages/plugins/<plugin-id>/plugin.ts`. It exports a static `descriptor` and
9
+ an executable `plugin` module.
10
+
11
+ The TypeScript descriptor remains the source of truth for permissions,
12
+ readable scopes, traffic mutations, workspace data, and default enabled state.
13
+ Runtime hook results are accepted only when the descriptor allows the matching
14
+ mutation.
15
+ Built-in local plugin modules are imported by the proxy from a static module
16
+ registry. Remote or downloaded plugin code is disabled.
17
+
18
+ ## Descriptor
19
+
20
+ ```ts
21
+ import type { PluginDescriptor } from "../../core/src/plugins/plugin-descriptor.ts";
22
+
23
+ export const descriptor: PluginDescriptor = {
24
+ id: "context-compressor-plugin",
25
+ name: "context-compressor-plugin",
26
+ type: "transformer",
27
+ category: "compression",
28
+ description: "Compresses large safe context and keeps bounded redacted excerpts locally.",
29
+ traffic: { reads: ["redacted-body", "audit"], mutates: ["transform"] },
30
+ permissions: ["body:read", "body:write", "audit:read", "audit:write"],
31
+ hooks: ["request:body:rewrite", "audit:manifest", "workspace:local-page"],
32
+ toggle: { defaultEnabled: false, canDisable: true },
33
+ modulePath: "plugin.ts"
34
+ };
35
+ ```
36
+
37
+ `canDisable` is always `true` for visible plugins. If a feature is required for
38
+ Molenkopf to run safely, it belongs in Core instead of the plugin catalog.
39
+
40
+ ## TypeScript Module
41
+
42
+ The public hook shape lives in `packages/core/src/plugins/plugin-api.ts`.
43
+
44
+ ```ts
45
+ import type { MolenkopfPluginModule } from "../../core/src/plugins/plugin-api.ts";
46
+
47
+ export const plugin: MolenkopfPluginModule = {
48
+ onBoot(ctx) {
49
+ ctx.note("plugin booted");
50
+ },
51
+ onStart(ctx) {
52
+ ctx.note("plugin started");
53
+ },
54
+ onRequest(ctx) {
55
+ return { body: ctx.body.toUpperCase(), notes: ["rewrote request"] };
56
+ },
57
+ getData(ctx) {
58
+ return { plugin: ctx.plugin, requests: ctx.manifests.length };
59
+ }
60
+ };
61
+ ```
62
+
63
+ Available hooks:
64
+
65
+ | Hook | Purpose |
66
+ | --- | --- |
67
+ | `onBoot` | Prepare local plugin state before the proxy listens. |
68
+ | `onStart` | Initialize enabled plugin work after the proxy listens. |
69
+ | `onEnable` | React to an admin enabling the plugin. |
70
+ | `onDisable` | React to an admin disabling the plugin. |
71
+ | `onRequest` | Inspect or transform a redacted request. |
72
+ | `onAudit` | Observe redacted audit manifests. |
73
+ | `onEvent` | Observe or emit local lifecycle events. |
74
+ | `getData` | Serve scoped workspace data. |
75
+ | `onStop` | Flush local state before shutdown. |
76
+
77
+ `getData` receives already-scoped, already-redacted audit manifests plus safe
78
+ workspace context such as the plugin view, declared scopes, and the memory graph
79
+ when available. The central proxy does not contain plugin-specific metrics.
80
+
81
+ ## Request Results
82
+
83
+ `onRequest` returns a result object instead of mutating global state directly.
84
+ The runner applies only the allowed fields:
85
+
86
+ ```ts
87
+ type PluginRequestResult = {
88
+ body?: string;
89
+ providerId?: string;
90
+ block?: { status: number; error: string };
91
+ notes?: string[];
92
+ redactedSecrets?: number;
93
+ compressedItems?: number;
94
+ savedTokens?: number;
95
+ retrievalIds?: string[];
96
+ compressorsUsed?: string[];
97
+ };
98
+ ```
99
+
100
+ If a plugin returns a body rewrite without `traffic.mutates` containing
101
+ `transform`, `mask`, or `augment-context`, the runner restores the previous
102
+ body and blocks with `plugin_capability_violation`.
103
+
104
+ ## Invariants
105
+
106
+ - Remote plugin loading is disabled.
107
+ - Core redaction runs before optional request plugins.
108
+ - Full prompts, full responses, credential values, cookies, and authorization
109
+ headers must not appear in plugin data, audit data, dashboard data, events, or
110
+ logs.
111
+ - Provider reroutes still pass provider, team, and key policy checks. Project is
112
+ key-level attribution, not a provider access layer.
113
+ - A disabled plugin must leave Molenkopf usable.