The MCP server and its 32 tools, and the plugin's skills, commands and hooks — what each one is, what it calls, and which screen in the Logrite app reflects it. Written from the source and from live calls against both estates, not from memory.
Updated 2026-10-08 for the 32-tool catalogue and plugin 1.2.0. The live
numbers in §01 and §09 were read 2026-10-05 against mcp.logrite.ai (workspace 252,
logrite-ops) and dev-mcp.logrite.ai (workspace 3103).
Logrite monitoring Logrite is two separate things, and they fail independently.
Two collector agents (logrite-prod-vm, logrite-dev-vm) run from
cron every five minutes under ~/logrite-agent/ and ship into
log-analytics-252-<agentKey>. Nobody has to do anything for this to work.
A developer in an editor, reaching the same workspace through MCP, writing one row per
call to mcp-activity-252. The tools were connected and authenticated; the
skills, commands and hooks were never loaded on any machine — see §02.
log-analytics-252-logrite-prod-vmlog-analytics-252-logrite-dev-vmlog-analytics-252-prod-probe-c0a219mcp-activity-2525.8 million documents on one side, 28 on the other. That ratio is the whole point of this page. The ingest path has been carrying the estate for weeks; the operating path had seven calls from a real editor in its entire history before this work (2026-09-29 → 2026-10-01), because the half of the product that makes an editor use those tools had never loaded.
curl -fsSL https://mcp.logrite.ai/install.sh | sh
The key comes from LOGRITE_API_KEY set on sh
(curl -fsSL https://mcp.logrite.ai/install.sh | LOGRITE_API_KEY=lgp_live_… sh),
otherwise from a prompt on /dev/tty with echo off. An empty answer, or no terminal
and no variable, installs in OAuth mode. The key is never a command-line argument — the
installer refuses one, because argv lands in shell history and in ps.
The script is served only by the MCP host, and it rewrites its own three default URLs
for the estate that served it — so dev-mcp.logrite.ai/install.sh validates against
the dev backend with no variables set.
| Step | What it produces | Why it is in that order |
|---|---|---|
| 1. validate | POST /api/v1/api-keys/introspect (key mode only) |
Before anything is written. A half-installed plugin pointing at a dead key fails later and
somewhere else. 401 and “no answer at all” are told apart, because they need
opposite actions. curl reads the header from stdin (-K -), never from argv. |
| 2. unpack | ~/.logrite/claude-marketplace/logrite/ + a generated
.claude-plugin/marketplace.json beside it with "source": "./logrite" |
A directory under ~/.claude/plugins/ with no marketplace registration is never
loaded by Claude Code. In OAuth mode the download is the generic plugin
(/skills-package?for=installer, no credential). |
| 3. legacy sweep | removes ~/.claude/plugins/logrite/ and an old
user-level copy of the init command, each only when it is recognisably ours |
Older installers put the payload where nothing reads it, and copied a command file that went stale. |
| 4. store the key | ~/.logrite/api-key (0600) and the headers helper
~/.logrite/mcp-headers.sh (key mode only) |
Claude Code runs the helper for the server's Authorization header, so the key is
in neither a process list nor Claude Code's MCP config. |
| 5. register | key mode: claude mcp add-json -s user logrite with
headersHelper; OAuth mode: claude mcp add -s user --transport http logrite
https://mcp.logrite.ai/sse | User scope, because an unscoped add binds the server to
whichever directory the installer happened to run in. In OAuth mode the user then runs
/mcp → logrite → Authenticate. |
| 6. load | claude plugin marketplace add → claude plugin install
logrite@logrite-local -s user | Skills, commands and hooks. The one init command is
/logrite:init. |
The zip from Sandbox → MCP installs the same way by hand; its root is the
logrite-local marketplace:
mkdir -p ~/.logrite && unzip logrite-claude-plugin.zip -d ~/.logrite/claude-marketplace
claude plugin marketplace add ~/.logrite/claude-marketplace
claude plugin install logrite@logrite-local -s user
Only one MCP registration, deliberately. The zip the installer downloads has
mcpServers and userConfig stripped from
plugin.json. If the plugin also declared the server you would get two registrations
of the same tools — logrite and plugin:logrite:logrite — the way
claude mcp list already shows claude.ai Slack beside
plugin:slack-by-salesforce:slack.
The fallback has to name what it lost. By the time the plugin install can fail, the MCP
server is already registered — so claude mcp list says Connected and the
install reads as complete while not one skill, command or hook is active. The installer prints
“the skills, the /logrite: commands and the hooks are NOT active” and the
commands to finish it.
| # | Defect | Evidence |
|---|---|---|
| D1 | The documented one-liner left skills, commands and hooks inert on
every machine it had ever run on. It unzipped to
~/.claude/plugins/logrite and then only ran claude mcp add. |
claude plugin list named only slack-by-salesforce@synced;
enabledPlugins was {}; known_marketplaces.json listed only
claude-plugins-official. |
| D2 | The advertised alternative was a repository that does not exist. Both the Sandbox’s Connect pill and the plugin’s own manifest pointed at it. | gh api repos/Deployd-io/logrite-claude-plugin → 404. |
Both are fixed. The manifest shipped inside the plugin now carries "source": "./"
— the marketplace manifest lives at <plugin root>/.claude-plugin/, so the
marketplace root is the plugin directory, which is the shape Anthropic’s own catalogue
uses for in-repo plugins. The Sandbox pill now shows the installer one-liner.
Verified on this machine, 2026-10-05: claude plugin list →
logrite@logrite-local, scope user, ✔ enabled;
claude mcp list → logrite … ✔ Connected with no second row; a real
claude session lists the /logrite:* commands and the skills.
stateless=True. One ASGI callable splits
on path: /sse is handled inline, everything else falls through to a separate
CORS’d Starlette app carrying /health, /skills-package and
/install.sh. CORS is on that app only — the MCP transport never grows a
preflight surface, because MCP clients are not browsers.Allow: POST, DELETE. A stateless server has no
server-to-client streams; answering 405 lets a client fall back to POST-only mode instead of
hanging on a stream that will never produce an event. verified live: 405/sse and
/skills-package. Chooses by prefix:
lgp_live_ / lgs_live_ → API key, anything else → JWT. The old prefix
is still accepted because a key minted before 2026-09 is a valid credential until it expires;
dropping it would not revoke those keys, it would make them unrecognisable.MCP_API_KEY_CACHE_TTL) keyed on
sha256(key). Accepted only when the reply carries both active and
workspaceId. A negative is never cached — it is evicted, so a revoked key
stops working promptly rather than at the end of a TTL.SECURITY_JWT_SECRET, base64-decoded only if the round-trip is
lossless — mirroring the backend. Workspace comes from the workspaceId claim; a
token without one is refused.finally. A contextvar rather than a
parameter because the MCP SDK owns the call path between the ASGI app and a tool handler. The
raw token rides on the identity in process only, because the platform tools call
Logrite as the caller — so each tool reaches exactly what that person reaches, and
nothing more.404 (“Unknown tool '…'. Call tools/list for the
available tools.”). Seven tools refuse an API key locally with
connected_client_required and a connectUrl, before any upstream call.
At most four cortex_chat / analyse_log_file /
run_compliance_scan calls run at once per identity; a fifth is
too_many_concurrent_calls (429), not queued. Every tool then goes out over HTTP to
the backend / AI service / semantic search / ingest shim; none talks to OpenSearch directly.
structuredContent is trimmed to 32 KB with truncated: true and
total. §04.mcp-activity-<ws>
MCP_ACTIVITY_QUEUE_MAX default 1000. Never on the request
path. An audit write that can fail a tool call turns logging into an outage; one that can
block a tool call turns a slow cluster into a hung editor. Overflow is counted and
logged, because a silent drop makes the trail look complete when it is not.An unreachable backend is 503, never 401. A failed introspection hop raises
BackendUnavailable, caught before the bare except. Telling a
developer their key is invalid while the backend is down sends them to create another key that
will not help either — and they conclude the product is broken rather than that a hop is. The
same split is honoured on /skills-package.
isError is not ok. Returning a bare list of content makes the
SDK answer isError: false, so “Cannot connect to OpenSearch” reached the
client looking like a result — and a model relays that to a developer as a finding about their
logs. Every failure now leaves as a CallToolResult with the flag set, and the trail
records ok:false, status:502 to agree with it. The live acceptance test used to pass
those tools for exactly the same reason: it checked the flag, and the flag said fine.
Every internal base URL is replaced by the service’s public name, an HTML error page is reduced
to its <title>, and everything is clipped to 300 characters — the reader is a
model with a context window. A result used to carry
"source": "http://backend:8080/…", a docker-network address that means nothing to a
customer and maps the estate for anyone who reads it; the path is kept, because that is
the public route a developer can call with the same key.
| Variable | Default | Why it exists |
|---|---|---|
MCP_API_KEY_CACHE_TTL | 60 | Seconds a verified key stays resolved in-process. |
MCP_ACTIVITY_ENABLED | true | Master switch for the trail. |
MCP_ACTIVITY_QUEUE_MAX | 1000 | Queue bound; overflow counted, never sampled. |
OPENSEARCH_VERIFY_CERTS | false | The cluster serves a self-signed certificate — the reason every runbook spells the curl -sk. Left verifying, every OpenSearch-backed tool failed CERTIFICATE_VERIFY_FAILED and silently emptied the activity trail. |
MCP_HTTP_USER_AGENT | logrite-mcp/1.0 | urllib’s default UA is answered 403 … 1010 by Cloudflare, which made every introspection fail and every tool call 503. |
MCP_PUBLIC_ESTATES | built-in 2 rows | Host → {mcp, backend, app}. Invalid JSON raises at import: loud at start-up beats a silently prod-pointing installer at request time. |
There is no auth bypass and there must not be one. No environment variable
short-circuits the credential check, a JWT without exp is refused, and a test fails if
a bypass reappears in code.
Nothing is filtered by credential, so tools/list is the same for everyone; what
differs is what each call reaches, because every tool carries the caller’s own token.
A grouped tool takes an action argument; arguments are camelCase and every
limit is capped at 100. A destructive action needs confirm equal to the
object's name, never its id. Tools tagged connected client refuse
an API key.
All 32 rows are listed below; the filter needs JavaScript and the table is complete without it.
| Tool | Family | R/W | Downstream |
|---|---|---|---|
get_workspace_context | Workspace | read | backend: GET /api/v1/project/workspace/{ws}, GET /api/v1/workspaces/{ws}/addons, GET /api/v1/warden/library/frameworks |
list_projects | Workspace | read | backend GET /api/v1/project/workspace/{ws} |
get_project | Workspace | read | backend GET /api/v1/project/{id} |
enrol_repository | Workspace | write | backend POST /api/v1/project/enrol; needs the Logrite GitHub App on the owner |
api_keys | Workspace | read write connected client | backend /api/v1/api-keys, …/{keyId}/rotate, DELETE …/{keyId}; introspect (allowed with a key) → /api/v1/api-keys/introspect |
project_admin | Workspace | read write | backend /api/v1/project/{projectId}, …/access, …/push-status, …/index-binding |
project_members | Workspace | read write | backend /api/v1/project/{projectId}/members, …/invitations/{id} |
workspace_addons | Workspace | read write | backend /api/v1/workspaces/{ws}/addons |
workspace_members | Workspace | read write | backend /api/v1/workspace/{ws}/members, …/seats, /api/v1/invite, /api/v1/workspace/role; the invite's workspace name comes from the credential |
workspace_settings | Workspace | read write | backend /api/v1/workspaces/{ws}/settings |
cortex_chat | Cortex | read | ai-service POST /api/v1/chat/sync |
knowledge_search | Cortex | read | ai-service POST /api/v1/knowledge/ask |
analyse_log_file | Logs | write | ai-service POST /api/v1/log-analyses (text/plain, x-filename) |
ingest_log_lines | Logs | write | ingest-shim POST /ingest, NDJSON, authed with a collector-agent token |
semantic_search | Logs | read | semantic-search POST /api/v1/search/hybrid |
analytics_query | Logs | read | backend OpenSearch proxy /api/v1/analytics; a foreign index is refused before the request |
collector_agent | Collectors | read write | backend /api/v1/agents; rotate is destructive, confirmed by the agent's name |
collector_telemetry | Collectors | read | backend /api/v1/agents/{agentId}/… |
list_compliance_frameworks | Compliance | read | backend GET /api/v1/warden/library/frameworks |
run_compliance_scan | Compliance | write | backend POST /api/v1/scan/initiate; poll /api/v1/scan-runs/latest |
scan_results | Compliance | read | backend /api/v1/scan-runs/…, /api/v1/scan-results/… |
compliance_library | Compliance | read write connected client | backend /api/v1/warden/library |
compliance_logs | Compliance | read | backend OpenSearch proxy /api/v1/analytics on compliance-logs-<ws>; actions search, services, summary |
maturity | Compliance | read write connected client | backend /api/v1/maturity |
warden_rules | AI Warden | read write connected client | backend /api/v1/warden/rulesets, /api/v1/custom-rules, /api/v1/warden/agents/{agentId}/… |
warden_relay | AI Warden | read write connected client | backend /api/v1/warden/relay-keys, …/requests, …/stats, …/violations/suppress |
integrations | Integrations | read write | backend /api/v1/integrations/discord, /api/v1/integrations/salesforce |
observability | Integrations | read write connected client | backend /api/v1/observability/connections, /api/v1/observability/feed |
get_mcp_activity | Audit | read | backend GET /api/v1/mcp/activity |
governance_audit | Audit | read connected client | backend /api/v1/governance |
logrite_connection | Connection | read | local for whoami, scopes, connect_url; backend /api/v1/mcp/connections |
coding_sessions | AI Coding | read write | backend /api/v1/coding/…; the session token only with includeToken |
No tool talks to OpenSearch directly. compliance_logs and
analytics_query go through the backend's OpenSearch proxy as the caller, so the
backend authorises every index against the caller's workspace a second time. The server's own
OpenSearch client is used only to write the activity trail and coding events.
The workspace comes from the verified credential and is never an argument. Two checks carry the boundary.
| Check | What it does |
|---|---|
belongs_or_shared(idx, ws) |
Applied to every index analytics_query is asked for: a foreign index is refused
before any request leaves the server. compliance_logs has no index argument at
all. |
| the backend's OpenSearch proxy | Authorises each request again against the caller's own token. Two layers, because a check in one service is a claim and the second one is a proof. |
Own workspace plus an explicit shared list — nothing else. There is no default tenant:
*-logrite indexes are refused like any other foreign index, and
top_queries-* is not shared.
The family list lives in two languages. mcp-activity and
compliance-logs are registered in the server’s WS_FAMILIES and
in the backend’s IndexNames. A one-sided edit is silent in both directions: absent
from the Java side, the OpenSearch proxy treats the family as “not a tenant index” and lets any
workspace read it; absent from the Python side, the activity read answers empty about a workspace
that is generating rows.
One document per JSON-RPC request, into mcp-activity-<workspaceId>.
{
"ts": "2026-10-05T11:02:00.621658+00:00",
"workspaceId": "252",
"userId": 8,
"credential": "api_key", // or "jwt"
"keyLast4": "cvEs", // null for a JWT
"method": "tools/call",
"tool": "get_workspace_context",
"argumentsDigest": "<sha256 of canonical JSON>",
"ok": true,
"status": 200, // 502 when the handler flagged the result
"durationMs": 5,
"resultBytes": 412,
"error": null,
"client": { "name": "claude-code/2.1.289 (sdk-cli)", "version": "" },
"sourceIp": "…"
}
The shape of the row a live claude session produced on this machine
on 2026-10-05, read back through get_mcp_activity.
Arguments are hashed, never stored. The digest is
sha256 of json.dumps(args, sort_keys=True, separators=(",", ":"),
default=str) — reproduce it with a bare sort_keys=True and it will not match.
A tool call carries customer log content and search terms; an audit index holding them is a second
copy of the data with none of the retention rules of the first, in an index nobody thinks of as
data. The backend strips any arguments field on the way out as a tripwire — its
comment notes that a field appearing there later would mean somebody had started to.
Key lifecycle does not go here. api_key_minted / rotated /
revoked are hash-chained GovernanceAudit rows in the backend. A coding
session issues hundreds of tool calls a minute; hash-chaining that would be the wrong
instrument.
client.name is the field that distinguishes an editor from a script: it reads
x-mcp-client-name, falling back to the user agent. Absent for a raw
curl — a fact worth recording rather than a reason to reject the call.
lr.sh session_start, then lr.sh flush (async)
flush ships any spooled events.lr.sh nudge, lr.sh prompt
knowledge_search instead of letting the model answer from memory.description — quoted in full below, because that string
is the trigger.lr.sh edit
lr.sh stop, then lr.sh flush
lr.sh session_end
Every hook fails open, and every hook needs .logrite/config.json.
lr.sh exits 0 silently with no python3, and lr_hook.py
swallows every exception. A hook that blocks a developer’s edit because a SaaS was unreachable
gets the plugin removed, and the developer is right to remove it. The corollary is that an
unenrolled repository produces silence, not an error.
| Skill | description — the string the model matches on | Tools, in the order the file states |
|---|---|---|
logrite-compliance |
“Use when a change touches audit logging, access control, data handling or retention in a Logrite-enrolled repository, or when the user asks which compliance frameworks apply, what a finding means, or whether a change will pass the pipeline.” | list_compliance_frameworks first → run_compliance_scan.
“If the call fails, say the frameworks could not be read. Do not fall back on the standard.” |
logrite-logging |
“Use when writing, reviewing or changing log statements in a repository enrolled with Logrite — choosing a level, deciding what to include, avoiding PII, and adding the correlation fields the workspace's compliance frameworks require.” | workspace_settings + list_compliance_frameworks before writing →
analyse_log_file to verify |
logrite-observability |
“Use when investigating a production problem, reading a log file, deciding what to instrument, or answering questions about what a service is doing — search, log analysis, and getting from a symptom to evidence with Logrite.” | A selection table, no fixed order: analyse_log_file, semantic_search,
analytics_query, knowledge_search, cortex_chat. |
worksoft-certify |
“Use when the question concerns Worksoft — Certify processes, recordsets, layouts, CTM suites, the Certify Results API, Worksoft Portal, Business Capture, Process Intelligence, Impact, or a log line from a Worksoft .NET service.” | workspace_addons → knowledge_search with
packs:["worksoft"] → relay the citation and the attribution line. Shipped only when
the workspace has the worksoft-cortex add-on. |
| Command | What it does | Tools it names |
|---|---|---|
/logrite:init | Enrol this repository. When enrolment is refused (400/409) or there is no remote, writes a local-only config with "projectId": null and points at Settings → Workspace → Source control → Link existing installation | get_workspace_context, enrol_repository, list_compliance_frameworks, workspace_addons, coding_sessions |
/logrite:log-review | Review the diff's logging for coverage, level and PII | list_compliance_frameworks, workspace_settings |
/logrite:compliance-check | Scan local changes, then optionally the pushed branch | list_compliance_frameworks then run_compliance_scan |
/logrite:explain-error | Explain an error or log line, grounded in the enabled packs | knowledge_search |
/logrite:add-logging | Add log statements in the workspace’s conventions | list_compliance_frameworks, workspace_settings |
/logrite:fix | Apply the proposed fix for a Logrite AI coding issue | coding_sessions |
/logrite:privacy | Show what Logrite records about AI coding sessions here, and how to opt out | none |
/logrite:uninstall | Undo everything /logrite:init wrote in this repository | none |
Type /logrite:init. The MCP prompt init
(/mcp__logrite__init) serves the same text: the prompt reads
plugin/commands/init.md on every call, so the two cannot drift.
What /logrite:init writes: .logrite/config.json and, only on an
explicit yes, a write-only session token in ~/.config/logrite/coding/ and a warn-only
git pre-commit block. Hooks: nothing to write. They are active as soon as the plugin is
installed. The key is never written into the repository — with the installer it lives in
~/.logrite/api-key — and .logrite/config.json is meant to be committed.
| Workflow | Screen | Reads | Visible / not visible |
|---|---|---|---|
| API key lifecycle | /sandbox → API keys drawerthe drawer has no route of its own |
/api/v1/api-keys, …/{id}/rotate, DELETE …/{id} |
Label, prefix…last4, Created, Created by, Last used, Expires.
Not the plaintext after mint, nor per-key call counts. |
| Every tool call | /sandbox/mcp → Activity |
GET /api/v1/mcp/activity ← mcp-activity-<ws> |
Six columns: When / Tool / Client / Duration / Outcome / Credential. Not
userId, sourceIp, argumentsDigest, status or
resultBytes — all present in the document, none rendered. No paging. |
| Cortex add-ons | three surfaces: /settings/addons, the Sandbox playground card, and the
chip on /analytics/logrite-cortex |
GET|PUT /api/v1/workspaces/{id}/addons |
One shared hook behind all three. Nothing MCP-specific: the UI never shows that a tool call consumed an add-on. |
| Key create / rotate / revoke | /m26-14 → Operate → Governance Audit |
GET /api/v1/governance/audit |
Newest 12 only, as action · actor and a timestamp. details,
rowHash and prevRowHash are persisted and never rendered; the
only chain affordance is a “Verify chain” button. |
run_compliance_scan |
/compliance/project/:id/scan/:scanId |
POST /api/v1/scan/initiate → /api/v1/scan-runs/latest |
Appears as an ordinary scan row. Unattributed — nothing says Claude Code started it. |
ingest_log_lines |
/analytics/discover |
the log-analytics-<ws>-<agentKey> documents |
Unattributed by construction: the tool sends only {"message": line} and the
shim derives workspace, service and index from the agent token and from nothing else. There is no
field a screen could key on. |
enrol_repository |
the project list on /compliance | GET /api/v1/project/workspace/{ws} |
The enrolled repository appears as an ordinary project. Unattributed. |
analyse_log_file |
nowhere | — | No collection GET exists for log analyses, and the UI never reads one by id either. |
“Last used” advances on every successful credential resolution — not on every call. A call rejected for a revoked or unknown key leaves it untouched, deliberately: a failed guess must not make a dead key look alive.
One empty state on the Activity tab is wrong, and it is the one workspace 252 hits. The “this workspace has no API key yet” branch is driven by whether this browser session minted a key, not by the workspace’s key list. On a workspace whose key was minted elsewhere — the normal case for a long-lived integration key — an empty filtered result renders “no API key yet” rather than “no tool call matches this filter”.
| # | What | Evidence | State |
|---|---|---|---|
| D1 | The documented install left skills, commands and hooks inert everywhere | claude plugin list named only the synced plugin; enabledPlugins {} |
fixed |
| D2 | The advertised marketplace repo does not exist | gh api repos/Deployd-io/logrite-claude-plugin → 404 |
fixed |
| D3 | list_compliance_frameworks is JWT-only, so an API-key caller can
never read the framework library — and three skills plus three commands open by calling it |
Live on prod today: “could not be read — the compliance library is served by the Warden control plane, which accepts only a signed-in session (a JWT)” | open |
| D4 | semantic_search defaulted to an index that does not exist |
was no such index [log-analytics-252]; collectors write
log-analytics-252-<agentKey> |
fixed |
| D5 | …and reported 0 hit(s) over a populated result set, because the
Response<T> envelope was never unwrapped before counting |
prose said 0 hit(s) beside 10 rows in structuredContent |
fixed |
| D6 | A downstream error came back with isError: false |
the no such index reply carried "isError":false |
fixed |
| D7 | enrol_repository appeared to post to a route prod did not serve |
Prod now answers POST /api/v1/project/enrol — a repository without the GitHub App
gets the backend's 409 “GitHub App is not installed” sentence, which only the served route
produces. |
fixed |
| D8 | run_compliance_scan could not pass validation — it posted to the
Gatekeeper CI route, whose DTO shares no field name with what the handler sent |
Now posts POST /api/v1/scan/initiate, which prod’s backend serves. The Gatekeeper
route was abandoned deliberately: it wants a CI diff payload and can open pull requests. |
fixed |
| D9 | semantic_search discarded the caller’s result count |
handler sent limit; the service declares k and an unknown body field
is dropped, so limit:1 returned 10 rows |
fixed |
semantic_search {"query":"error"} → 10 hit(s) … in log-analytics-252-* (D4, D5)
semantic_search {"query":"error","limit":3} → 3 rows in structuredContent (D9)
semantic_search {"query":"error","index":"does-not-…"} → isError: true (D6)
knowledge_search {"packs":["worksoft"]} → isError: true, ADDON_NOT_ENABLED,
the backend's own wording preserved
D3 costs more than its size suggests. logrite-compliance opens with “Call
list_compliance_frameworks before answering anything”, logrite-logging
requires it too, and /logrite:compliance-check, /logrite:add-logging and
/logrite:log-review each name it first. The refusal is at least honest and explains
itself — but until that route accepts an API key, the compliance half of this integration reads
its frameworks in the browser or not at all.
An add-on refusal is flagged, and keeps the backend’s words. A 403 from
knowledge_search arrives as isError: true carrying
ADDON_NOT_ENABLED and the backend’s exact sentence — “This workspace has not
enabled Worksoft. Turn it on in Settings → Cortex add-ons…”. The reasoning is that the
question was not answered, and an unflagged refusal reads as an answer; rewording it into “no
results” is how an entitlement problem becomes an unanswerable support ticket.
| Workspace | 252 — logrite-ops, owned by the ops service
account, userId 8. The address is deliberately not written out: Cloudflare's
email obfuscation rewrites any user@host literal into a
/cdn-cgi/l/email-protection link and injects an external decoder script —
which would both break this page's one hard rule and leave the address unreadable with
JavaScript off. |
|---|---|
| Credential | claude-code-local, last four cvEs, scope
production, expires 2027-09-29. Prod only — the same key is 401 on the dev
backend, which is correct and not a fault. |
| Collectors | logrite-prod-vm and logrite-dev-vm under
~/logrite-agent/, in the crontab every five minutes |
| Enrolled repo | LogriteMasterControl — .logrite/config.json
with projectId: null: a local-only enrolment, the shape /logrite:init
writes when the backend refuses enrolment. |
The key is never printed anywhere. Not in this page, not in the installer’s output, not in a prompt body — a prompt body ends up in a transcript, and a transcript is copied into tickets. The last four characters are an identifier; the key is not.