# Connector bot: bring selected knowledge into your vault OrgVault Connector Bot **1.0.0** runs on an administrator's Mac, Linux machine, or on-premises infrastructure using Node.js 22+ or Docker. It reads selected information from Attio, Granola, Fireflies, or a compatible JSON feed; applies local rules; asks your chosen LLM to classify and format allowed content; and writes Markdown into explicitly configured private OrgVault folders. Download the [connector package](/downloads/orgvault-connector.zip), read the [agent deployment instructions](/connectors-deploy.md), and verify its SHA-256 against [package metadata](/api/v1/connectors). The package includes code, example configuration, Docker/Compose files and both guides. There is no npm install step. The code is licensed for running and adapting this connector for your organization's use of hosted OrgVault. The administrator runs and operates the connector. OrgVault does not deploy it, collect integration keys, proxy LLM requests, or monitor its local process. No third-party integration is activated by opening the console or downloading the package. OrgVault itself remains a hosted service. ## Data flow and control 1. A read-only source adapter fetches content in bounded pages. 2. Local rules reject blocked phrases, common credential patterns, oversized items, and content outside the literal topic allowlist. Configured literal redactions apply before the LLM sees the item. 3. The bot reads the destination `spec.md` and accessible ancestor specifications. Your selected LLM receives the filtered title/content, these folder specifications and your formatting instructions. It returns `publish`, `skip`, or `review`, plus a category and Markdown. It has no tools or credentials. 4. Local validation rejects unexpected categories, malformed/truncated responses, blocked phrases, redaction terms, HTML, images and links. `review` holds an item out of OrgVault; it is not a human approval queue. 5. Approved Markdown goes to the matching configured folder using the bot's private-key-signed requests. Existing OrgVault grants, classification, billing access, legal holds and retention still apply. No public-post API is called. **An LLM cannot guarantee that information is non-confidential.** Keyword checks and model instructions are safeguards, not a complete data-loss prevention system. Content that passes local checks is sent to the chosen model endpoint, which may be outside your premises. Use source permissions and narrow folders/records first; use an approved local OpenAI-compatible endpoint when raw content must stay on premises. Administrators should review previews and refine rules before permitting automated publication. Do not enable a source key that can read material the connector should never process. ## Sources | Source | Imported content | Administrator controls | | --- | --- | --- | | Attio | Record notes, title and Markdown/plain text | Scoped access token; optional `parent_object` and `parent_record_id` | | Granola | Completed meeting summaries and titles | Personal/workspace API key according to your plan and permissions; optional `folder_id` including descendants | | Fireflies | Completed meetings, summary and action items | API key; optional `organizers` email list; `include_transcript:true` adds transcript text | | JSON feed | Your own normalized text items | Explicit HTTPS endpoint, bearer key and cursor pagination | Granola's private notes, attendees and transcripts are excluded. Fireflies transcript import is off by default. Attio CRM record fields, emails, tasks, audio, attachments and source writes are outside this version's scope. Granola's native Attio sync can duplicate the same meeting across sources: choose one source for that material. Deduplication is by source instance and source item ID, not semantic similarity across products. API references checked for this implementation: [Attio notes and required read scopes](https://docs.attio.com/rest-api/endpoint-reference/notes/list-notes), [Granola API and key setup](https://docs.granola.ai/introduction), [Granola summaries](https://docs.granola.ai/api-reference/get-note), [Granola pagination](https://docs.granola.ai/api-reference/list-notes), [Fireflies transcripts](https://docs.fireflies.ai/graphql-api/query/transcripts), and [Fireflies authentication](https://docs.fireflies.ai/fundamentals/authorization). Your source provider may require a paid plan or administrator-enabled API access. ## Configuration version 1 Start with `config.example.json`. Delete sources you do not use, fill in the organization ID and model, and keep keys in environment variables named by `api_key_env`. Unknown configuration fields are rejected. Paths to local identity/state are relative to the configuration file. - `poll_minutes`: **30** by default, 1–1440. The next interval starts after the previous cycle finishes; sleeping/offline machines do not poll. - `mode`: `preview` initially. `once` and `run` require an explicit change to `publish`. - `initial_sync_days`: 30 by default. Granola, Fireflies and JSON feeds initially include this period. Later daily reconciliation scans from that fixed starting date; other cycles revisit `lookback_days` (default 7). Older source changes outside the initial boundary are not imported. Attio scans all notes accessible to the configured token/record because its notes endpoint has no equivalent date filter in this adapter. - `max_pages_per_poll`: 5 per source. Interrupted/budget-limited pages resume on the next poll. Attio and Fireflies use 50-row pages. A failing item keeps its page pending until resolved; status/logs expose this backlog. Offset pagination is best effort when a source changes during a scan; subsequent reconciliation repairs omissions. - `rules.include_any`: required nonempty list of case-insensitive literal topic matches. `exclude_any` blocks matching source and output. `redact_terms` removes literal terms from model input and rejects them in output. `instructions` defines semantic restrictions; `format` defines the desired Markdown layout. - `routes`: exact allowed category → folder prefix and classification (1 internal, 2 confidential, 3 restricted). No root `/` route, path traversal or model-selected folder. Readers of these folders are determined by OrgVault grants, not the category name. Use separate narrowly granted bots/configurations when destinations have different readership. - `max_llm_calls_per_poll` / `max_llm_calls_per_day`: 20 / 100 by default. These count attempts, including failures and paid previews; state persists the UTC daily counter before sending a request. They are not dollar budgets. Set a spend limit with your provider as well. Input/output size and output-token limits bound individual calls. - `max_state_entries`: 50,000 by default. The connector stops adding new item records at the limit rather than silently discarding deduplication history. Attio/Granola/Fireflies API origins are fixed. JSON-feed and compatible-model URLs must use HTTPS, or loopback HTTP for local services; redirects are refused. Only API keys referenced by the active source/model are used in their respective requests. Do not embed secrets in URLs, rules, models or configuration fields. ## Model choice Select the model explicitly; there is no default paid model or OrgVault LLM charge. `openai` uses Chat Completions with `store:false`, bounded `max_completion_tokens`, and optional strict JSON schema. [OpenAI request contract](https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create). `anthropic` uses `https://api.anthropic.com/v1/messages`, `max_tokens`, and local JSON validation. [Anthropic request contract](https://platform.claude.com/docs/en/api/messages/create). For another provider or local model, use `openai-compatible`, the complete chat-completions endpoint, and `json_schema:false` if unsupported. Compatible endpoints must implement `messages`, `max_tokens`, `choices[0].message.content` and `finish_reason:"stop"`. Native Gemini and arbitrary provider protocols are not implemented; a compatible gateway can be used. Model refusals, tool calls, malformed output and truncation never publish. ## Delivery, editing and retention Each item maps to `PREFIX/SOURCE_ID-SHA256_SOURCE_ITEM.md`. Content/policy hashes avoid repeated LLM calls for unchanged processed items. Source edits are checked again against current rules. An atomic local outbox saves approved Markdown before upload; uncertain writes are reconciled by reading the destination and comparing its content hash. Version-checked writes prevent blind overwrites. Human/bot edits to a managed file cause a conflict; deleted/retention-purged managed files are not automatically recreated. Duplicate commits with identical content are accepted without another revision. Never run two connectors against the same identity/state; lock files enforce this locally. Changes to routes leave previously imported copies where they were. Source deletion, permission loss, or a newly restrictive rule **does not delete previously imported files**. The connector has no deletion privilege; an administrator must review old imports, their OrgVault retention/legal-hold policy, and any cleanup separately. Do not erase state to resolve a conflict: doing so loses ownership hashes and retry history. Renaming a source ID creates a distinct source instance. State/outbox and optional previews are private local files, not encrypted by this package. Restrict the machine account, enable disk encryption, back up identity/state securely and remove previews when reviewed. A successful upload removes its Markdown from the outbox; metadata hashes remain. Stop the process before rotating keys, editing config or restoring state. Existing invitation expiry and revocation are honored: refreshing a session cannot extend the administrator's original access grant. Review expiring access before unattended operation. ## Additional sources: JSON feed v1 The connector calls `GET ENDPOINT?since=ISO&until=ISO&limit=50[&cursor=OPAQUE]` with its configured bearer key. Your service must return `{ "items": [{ "id": "stable-id", "title": "Client update", "content": "Markdown or text" }], "next_cursor": null }`, or a nonempty cursor for another page. Maximum 50 items; IDs must remain stable. Honor the fixed date window and return updates consistently when a window is rescanned. Cursors are passed only as query values to the same endpoint. No returned URL is followed. Add custom adapters locally by extending `connector-sources.mjs` and the config validator; preserve read-only fetching and the shared policy/publishing pipeline. ## Folder specifications and indexes Follow the [global folder convention](/folders.md). Run `init-folders` once as an explicit setup step to create missing `spec.md` and `index.md` files in writable configured folders and ancestors. Review those files before using the LLM. The poller requires the destination specification, reads accessible parent specs, and includes their content in its change fingerprint. These specs guide formatting but cannot override local rules, fixed destinations or access. Input limits include spec text; each spec is limited to 12,000 characters and the combined set to 60,000. After uploading an item, the connector updates its own marked section of the destination index using version-checked merges. It preserves other entries and never overwrites an existing specification. Until the index update succeeds, the durable upload remains pending and is reconciled on the next poll. Model-generated body links remain disallowed; index links are generated deterministically to the bot’s own private files. Maintain the same readership/classification for a destination’s index and its imported files. There is no automatic backfill of existing organizations’ folder specifications. The connector reads the destination index before formatting and provides the model a maximum 4,000-character excerpt, marked when truncated. Index entries are directory context, never instructions. Only specification changes invalidate previous formatting; index updates alone do not cause repeat model calls. The complete index is still used for version-checked merges.