openfused 0.3.10 → 0.3.12

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.
package/README.md CHANGED
@@ -241,10 +241,20 @@ The daemon has two modes:
241
241
  # Full mode — serves everything to trusted LAN peers
242
242
  openfused serve --store ./my-context --port 9781
243
243
 
244
- # Public mode — only PROFILE.md + inbox (for WAN/tunnels)
244
+ # Public mode — PROFILE.md + inbox + outbox pickup (for WAN/tunnels)
245
245
  openfused serve --store ./my-context --port 9781 --public
246
246
  ```
247
247
 
248
+ Public mode endpoints:
249
+
250
+ | Endpoint | Method | Purpose |
251
+ |----------|--------|---------|
252
+ | `/` | GET | Service info |
253
+ | `/profile` | GET | Your PROFILE.md (public address card) |
254
+ | `/config` | GET | Your public keys (JSON) |
255
+ | `/inbox` | POST | Accept signed messages (rejects invalid signatures) |
256
+ | `/outbox/{name}` | GET | Pickup replies addressed to `{name}` (encrypted) |
257
+
248
258
  ## File Watching
249
259
 
250
260
  `openfuse watch` combines three things:
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import * as registry from "./registry.js";
8
8
  import { fingerprint } from "./crypto.js";
9
9
  import { resolve, join } from "node:path";
10
10
  import { readFile } from "node:fs/promises";
11
- const VERSION = "0.3.10";
11
+ const VERSION = "0.3.12";
12
12
  const program = new Command();
13
13
  program
14
14
  .name("openfuse")
@@ -544,7 +544,12 @@ program
544
544
  const reg = registry.resolveRegistry(opts.registry);
545
545
  try {
546
546
  const manifest = await registry.discover(name, reg);
547
- // Auto-import key + add as peer so `openfuse sync` works for replies
547
+ // Auto-import key (untrusted) + add as peer so future `openfuse sync` can
548
+ // deliver replies and pull context. Key is deliberately NOT trusted — the user
549
+ // must explicitly `openfuse key trust <name>` after out-of-band verification.
550
+ // NOTE: manifest data comes from the registry and is attacker-controlled.
551
+ // The endpoint URL is stored as-is; a malicious entry could point at an internal
552
+ // service. Sync will pull from it — consider validating URL scheme/host.
548
553
  let config = await store.readConfig();
549
554
  if (!config.keyring.some((e) => e.signingKey === manifest.publicKey)) {
550
555
  config.keyring.push({
package/dist/mcp.js CHANGED
@@ -23,7 +23,7 @@ const storeDir = process.env.OPENFUSE_DIR || process.argv[3] || ".";
23
23
  const store = new ContextStore(resolve(storeDir));
24
24
  const server = new McpServer({
25
25
  name: "openfuse",
26
- version: "0.3.10",
26
+ version: "0.3.12",
27
27
  });
28
28
  // --- Context ---
29
29
  server.tool("context_read", "Read the agent's CONTEXT.md (working memory)", async () => {
@@ -1,5 +1,5 @@
1
1
  import { ContextStore } from "./store.js";
2
- export declare const DEFAULT_REGISTRY = "https://openfuse-registry.wzmcghee.workers.dev";
2
+ export declare const DEFAULT_REGISTRY = "https://registry.openfused.dev";
3
3
  export interface Manifest {
4
4
  name: string;
5
5
  endpoint: string;
package/dist/registry.js CHANGED
@@ -7,8 +7,8 @@
7
7
  // This is TOFU (Trust On First Use) done right: the registry distributes keys,
8
8
  // but never asserts trust. Trust is a local decision.
9
9
  import { signMessage, fingerprint } from "./crypto.js";
10
- // Custom domain pending propagation use Worker URL as fallback
11
- export const DEFAULT_REGISTRY = "https://openfuse-registry.wzmcghee.workers.dev";
10
+ // Registry API lives on .dev (product layer). DNS TXT records on .net (protocol layer).
11
+ export const DEFAULT_REGISTRY = "https://registry.openfused.dev";
12
12
  export function resolveRegistry(flag) {
13
13
  return flag || process.env.OPENFUSE_REGISTRY || DEFAULT_REGISTRY;
14
14
  }
package/dist/store.js CHANGED
@@ -172,10 +172,10 @@ export class ContextStore {
172
172
  const entry = config.keyring.find((e) => e.name === peerId || e.address.startsWith(`${peerId}@`));
173
173
  let signed;
174
174
  if (entry?.encryptionKey) {
175
- signed = await signAndEncrypt(this.root, config.id, message, entry.encryptionKey);
175
+ signed = await signAndEncrypt(this.root, config.name, message, entry.encryptionKey);
176
176
  }
177
177
  else {
178
- signed = await signMessage(this.root, config.id, message);
178
+ signed = await signMessage(this.root, config.name, message);
179
179
  }
180
180
  // Envelope filename encodes routing metadata so sync can match outbox files to peers
181
181
  // without parsing JSON. Colons/dots replaced to stay filesystem-safe across OS.
package/dist/sync.js CHANGED
@@ -153,7 +153,10 @@ async function syncHttp(store, peer, baseUrl, peerDir) {
153
153
  errors.push(`${dir}/: ${e.message}`);
154
154
  }
155
155
  }
156
- // Pull peer's outbox for messages addressed to us (HTTP version)
156
+ // Pull peer's outbox for messages addressed to us (HTTP version).
157
+ // The peer's daemon filters to _to-{myName}.json, but the JSON body is untrusted —
158
+ // a malicious peer controls every field. We must sanitize before using any value
159
+ // in a filename to prevent path traversal (e.g. from="../../.keys/evil").
157
160
  const config = await store.readConfig();
158
161
  const myName = config.name;
159
162
  const inboxDir = join(store.root, "inbox");
@@ -165,7 +168,11 @@ async function syncHttp(store, peer, baseUrl, peerDir) {
165
168
  for (const msg of messages) {
166
169
  const ts = (msg.timestamp || new Date().toISOString()).replace(/[:.]/g, "-");
167
170
  const from = msg.from || "unknown";
168
- const fname = `${ts}_from-${from}_to-${myName}.json`;
171
+ // SECURITY: sanitize remote-controlled values before constructing local filenames.
172
+ // Without this, a malicious "from" like "../../.keys/x" could write outside inbox/.
173
+ const safeFrom = from.replace(/[^a-zA-Z0-9\-_]/g, "");
174
+ const safeTs = ts.replace(/[^a-zA-Z0-9\-_]/g, "");
175
+ const fname = `${safeTs}_from-${safeFrom}_to-${myName}.json`;
169
176
  const dest = join(inboxDir, fname);
170
177
  if (!existsSync(dest)) {
171
178
  await writeFile(dest, JSON.stringify(msg, null, 2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openfused",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "description": "The file protocol for AI agent context. Encrypted, signed, peer-to-peer.",
5
5
  "license": "MIT",
6
6
  "type": "module",