Skip to content
曾亮懿 ZengLiangYi Blog

Team knowledge sharing: tokens, approval, and instance boundaries

Separate current cloud tokens from proposed member permissions; test read operations and batch approval on fixtures and define recovery responsibilities.

曾亮懿

曾亮懿

4 min read
Table of contents Show Hide

Team sharing has two boundaries: what content is approved to leave a personal device, and which principals may perform which operations on a shared instance. ChatCrystal’s cloud mode already has token authentication and normalized remote import. The pinned commit does not create separate member accounts or project-level read/write roles. Distributing one shared token does not create permission isolation.

Current cloud capabilities

server/src/runtime/cloud.ts enables cloud mode through CHATCRYSTAL_CLOUD_MODE=true or a nonempty CHATCRYSTAL_API_TOKEN. server/src/routes/setup.ts installs Bearer-token checks for protected API routes. Health, setup status/completion, and auth verify are public exceptions; setup, verification, and token rotation have their own checks. The rotate route verifies currentToken in its request body, so not every route is guarded only by Bearer authentication. server/src/services/auth.ts supports an environment token or a scrypt-stored single token and failed-attempt controls. The API rotates only a persisted token; environment tokens must be changed in the deployment environment. This is not multiple accounts, individual revocation, or RBAC.

server/src/routes/import.ts disables server-side local scans in cloud mode and provides ingest. server/src/cli/commands/import.ts parses on a device holding source histories and uploads a normalized payload; noninteractive use requires explicit –yes. server/src/runtime/watcherPolicy.ts prevents cloud-mode watching. Neither automatic scanning of everyone’s desktop from a shared host nor a complete absence of remote import matches current behavior. Multiple clients can submit; concurrency pressure, isolation, and conflict recovery are not thereby tested.

The seven MCP tools use the same API connection through a client; see MCP protocol boundaries. Tool names do not identify the team’s dataset or establish member permissions for a gateway.

Define the trust boundary before choosing a layout

Layout Boundary retained Additional work
Personal libraries plus reviewed readable exports Private histories remain on their owner’s device first Export versions, redaction, provenance, updates
Shared instance within one trusted group Current single-token access Holder capabilities, rotation impact, concurrent recovery
Shared instance behind an identity gateway External member/role policy can be defined Complete method/path tests, direct-access prevention, gateway maintenance and audit
Separate instances for different trust groups Data and credentials stay separate Multi-instance updates and retrieval strategy

These are deployment designs, not built-in team UI features or measured member-count and memory recommendations. tag, project_key, and scope describe retrieval or knowledge semantics, not token permission partitions. Manual global memory versus automatic project memory cannot promise that a member cannot read another project.

Review content first and define reads by operation

Source histories may include paths, code, credentials, or unreviewed conclusions. Obtain the owner’s approval, define shareable scope, redact, and record provenance before uploading. Automatic summaries do not guarantee redaction. Server ingest is a data entry point; command confirmation is not organizational approval.

GET-only access blocks read-like operations such as POST /api/memory/recall, while potentially exposing sensitive configuration GETs. Define roles against exact methods and paths, then test actual client workflows. Prevent direct access to the shared-token backend at the network layer. The following checks a proposed gateway policy, not ChatCrystal authentication. Roles are parameters; there is no identity authentication, HTTP proxy, real redactor, import, or backend.

Save this as team.mjs under Node 24 and run node team.mjs.

import assert from 'node:assert/strict';

// Proposed gateway policy; this is not ChatCrystal's token hook.
const rules = new Map([
  ['reader', new Set(['POST /api/memory/recall', 'GET /api/notes'])],
  ['importer', new Set(['POST /api/import/ingest'])],
]);
function authorize(role, method, rawPath, target, approvedTarget) {
  assert.equal(target, approvedTarget);
  const url = new URL(rawPath, 'https://gateway.invalid');
  if (url.pathname.includes('%') || url.pathname.includes('..')) throw new Error('ambiguous path');
  if (!rules.get(role)?.has(`${method} ${url.pathname}`)) throw new Error('denied');
}
const target = 'reviewed-instance-a';
let stored = [];
function ingest(role, items) {
  authorize(role, 'POST', '/api/import/ingest', target, target);
  for (const item of items) {
    if (item.approval !== 'approved' || item.classification !== 'team-shareable')
      throw new Error('unapproved input');
    if (/secret|password|token/i.test(item.content)) throw new Error('redaction required');
  }
  stored.push(...items.map(({ id, content }) => ({ id, content })));
}
authorize('reader', 'POST', '/api/memory/recall', target, target);
assert.throws(() => authorize('reader', 'POST', '/api/import/ingest', target, target), /denied/);
assert.throws(() => authorize('reader', 'GET', '/api/notes', 'other-instance', target));
assert.throws(() => authorize('reader', 'DELETE', '/api/notes/1', target, target), /denied/);
assert.throws(() => authorize('reader', 'POST', '/api/queue/cancel', target, target), /denied/);
assert.throws(() => authorize('reader', 'GET', '/api/config', target, target), /denied/);
assert.throws(() => authorize('unknown', 'GET', '/api/notes', target, target), /denied/);
const approved = {
  id: 'reviewed-1',
  content: 'use a scoped transaction',
  approval: 'approved',
  classification: 'team-shareable',
};
ingest('importer', [approved]);
assert.equal(stored.length, 1);
assert.throws(
  () => ingest('importer', [approved, { ...approved, id: 'private-2', approval: 'pending' }]),
  /unapproved/
);
assert.equal(stored.length, 1);
assert.throws(
  () => ingest('importer', [{ ...approved, content: 'password=example' }]),
  /redaction/
);
assert.equal(stored.length, 1);
console.log('proposed gateway roles and whole-batch review gate pass on fixtures');

Permission and approval checks need rejection fixtures

Remove rule matching: the reader-ingest rejection assertion must fail. Move stored.push before reviewing all inputs: the pending-content batch pollutes synthetic storage and the count assertion must fail. Restore and run again. Keyword checks only reject the example; they are not dependable redaction or sensitive-data detection. URL/method rules are not a complete secure proxy implementation.

Before deployment, separately test missing/wrong tokens, old tokens after rotation, reader recall and denied writes, unapproved payloads, the wrong instance, gateway bypass, and retrieval after restore. Receipts in server/src/services/memory/writeback.ts and quality feedback in server/src/services/experience/reviews.ts serve business purposes; neither is a complete per-member operation audit. A shared token cannot reliably identify the person acting. If external systems own identity, audit, and content approval, record their owners and retention policy in the deployment agreement.

Synchronization and backup cannot copy an active database blindly

Do not point multiple desktops at one active chatcrystal.db or overwrite a running library through file synchronization. sql.js operates in memory and exports data; vectra-index is another dataset. See WASM persistence and vector-index boundaries. Stop all writers or take a consistent snapshot, copy the agreed data directory, and verify recovery in isolation. Active source-tool SQLite also needs main-database/WAL consistency; copying one state.vscdb alone does not establish a complete backup.

Sharing does not require centralizing every history. Test approved imports, provenance, retrieval, rejection, and recovery for the same task before choosing what enters a shared library. See the CLI knowledge workflow for automation steps.

Share:

Related Posts

Review AI coding conversations: denominators, quality, and reuse

Audit source and length denominators, false accepts and rejects, and empty samples; separate current filtering, summaries, and measured reuse.

曾亮懿 曾亮懿
5 min read
#ChatCrystal #Knowledge management #Evaluation

Getting started with ChatCrystal: install, import, and search

Verify installation, the local service, both model configurations, import, summarization, and search with the actual ChatCrystal 0.6.1 CLI.

曾亮懿 曾亮懿
4 min read
#ChatCrystal #Node.js #Knowledge management

Importing Codex, Trae, and Copilot: verify each history format

Check source directories, event extraction, and snapshot boundaries in ChatCrystal, then verify each adapter against a conversation you know.

曾亮懿 曾亮懿
4 min read
#ChatCrystal #Data parsing #Knowledge management

From writing to code

Explore ChatCrystal, developer tools and daily learning notes on my GitHub. Questions about the implementation are welcome.