Skip to content

Add secure Proto AI chat - #761

Draft
mcharles-square wants to merge 13 commits into
mainfrom
feat/ai-chat-poc
Draft

Add secure Proto AI chat#761
mcharles-square wants to merge 13 commits into
mainfrom
feat/ai-chat-poc

Conversation

@mcharles-square

@mcharles-square mcharles-square commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Reviewable diff: +3193/-3 across 34 files (excludes generated, test, and story files).

Summary

Proto Fleet gains a permission-aware Proto AI chat launched from a floating action button, with streamed agent activity, conversational history, and structured Markdown responses. Operators configure the agent under Settings > Agents by selecting a harness and an OpenAI, Anthropic, or Ollama provider, discovering compatible models dynamically, and storing provider credentials encrypted on the Fleet server. The embedded agent is intentionally read-only, bounds tool execution, and minimizes fleet data before sharing tool results with an external provider.

How it works

  1. An operator with API-key management permission selects an agent harness and provider, supplies credentials when required, and chooses from the provider's discovered model list.
  2. The Chat service validates the destination, encrypts the credential, and persists the configuration for the operator's organization without returning the secret to the client.
  3. A fleet reader opens Proto AI from the global FAB and sends a message with the current client-side conversation history.
  4. The server-streaming chat RPC runs the embedded agent loop. Tool calls reuse existing Fleet, Sites, and Pools handlers so their established authorization and data-access rules remain in force.
  5. Each request is limited to four tool calls per completion and eight overall. Equivalent calls reuse one backend result, and pool results expose names only—not URLs, usernames, wallet identifiers, or worker identifiers.
  6. Compact activity events and assistant text stream back into the conversation. Markdown tables are rendered as structured tables when the answer contains tabular fleet data.
flowchart LR
  O["Operator: Settings > Agents"] --> C["Chat configuration RPCs"]
  C --> S["Organization-scoped encrypted provider config"]
  C --> P["OpenAI, Anthropic, or Ollama model discovery"]
  U["Fleet reader: Proto AI panel"] --> M["SendMessage server stream"]
  M --> A["Bounded read-only agent loop"]
  A --> T["Existing Fleet, Sites, and Pools handlers"]
  T --> A
  A --> L["Configured LLM provider"]
  L --> A
  A --> E["Activity and assistant text events"]
  E --> U
Loading

Areas of the code involved

Area / package / file What changed Why it matters for review
proto/chat/v1 and generated clients Added configuration, model-discovery, and streaming chat contracts Defines the client/server boundary; generated files can be skipped
server/internal/domain/chat Added configuration resolution, provider adapters, bounded tool execution, model filtering, generic provider errors, and outbound egress controls Core agent behavior, provider compatibility, SSRF prevention, secret handling, and backend load limits
server/internal/handlers/chat Added permissioned configuration/chat RPC handlers and data-minimized fleet inspection tools Determines who can configure or use chat and what fleet data may reach a model provider
server/migrations, server/sqlc Added organization-scoped encrypted LLM configuration persistence Review encryption lifecycle, tenancy, and migration reversibility
Server wiring and interceptors Registered the service, permissions, deployment-owned Ollama egress configuration, and sensitive request-body handling Ensures the RPC surface is exposed without logging credentials or granting API users private-network access
client/src/protoFleet/features/aiChat Added the FAB, slide-up panel, status rows, chat state, cancellation, and structured message rendering Primary operator experience and streaming-state behavior
client/src/protoFleet/features/settings/agents plus routing/navigation Added the Agents submenu and harness/provider/model configuration UI with automatic discovery Operator setup flow and the no-default-provider behavior
Tests Added client component/store coverage and Go domain, provider, egress, handler, permission, disclosure, and tool-budget coverage Regression protection for the main UX and security-sensitive paths

Key technical decisions & trade-offs

  • The embedded harness is the working execution path; Goose ACP is selectable but returns an explicit unavailable error until a server adapter is deployed.
  • Conversation history remains client-side and is sent with each request, avoiding server-side chat retention at this stage.
  • The UI supports OpenAI, Anthropic, and Ollama only; custom providers remain out of scope to keep discovery and credential rules explicit.
  • Models are discovered automatically on provider selection or settings load. OpenAI results are filtered to models compatible with this chat/tool flow.
  • Temperature is fixed internally; it is omitted for GPT-5-family OpenAI requests that reject non-default values.
  • Public providers require HTTPS and public destinations. Ollama permits loopback by default; RFC1918/private destinations require the deployment owner to set CHAT_ALLOW_PRIVATE_OLLAMA=true. Link-local, metadata, reserved, redirect, and DNS-rebinding paths remain blocked.
  • Provider-controlled response bodies and network details are not returned through Fleet errors; clients receive only generic failure context and HTTP status codes.
  • Pool tool results contain names only. Connection URLs, usernames, wallet identifiers, worker identifiers, and credentials never enter the external model context.
  • Agent execution allows four tool calls per completion and eight per request, and equivalent calls share one backend result.
  • Agent tools delegate to existing read-only handlers instead of introducing a parallel fleet data-access layer.

Testing & validation

  • Client: 22 targeted Vitest tests passed across the chat panel, structured message renderer, activity status, settings, and layout/navigation coverage.
  • Server: go test ./cmd/fleetd ./internal/domain/chat ./internal/handlers/chat passed, including Ollama policy, provider-error redaction, pool disclosure, deduplication, and tool-budget regressions.
  • Lint: just lint passed across Buf, client ESLint, server golangci-lint, and affected plugins.
  • Pre-push validation: server lint passed for the security hardening commit; the earlier full feature push also passed client typecheck.
  • Not covered: browser E2E against live provider credentials, a private-network Ollama deployment with the explicit opt-in, or a deployed Goose ACP harness.

@github-actions github-actions Bot added javascript Pull requests that update javascript code client server shared review-policy: needs-review Managed by the Review Policy workflow. labels Jul 16, 2026
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated security-focused code review generated by Codex.
It should be used as a supplementary check alongside human review.
False positives are possible - use your judgment.

Scope summary

  • Reviewed pull request diff only (c1d5b373abb4138d5ccf7624cb7dfaba3a67582b...7728f56efabbfed1192a1417982f39cc49e11c62, exact PR three-dot diff)
  • Model: gpt-5.5

💡 Click "edited" above to see previous reviews for this PR.


Review Summary

Overall Risk: MEDIUM

Findings

[MEDIUM] Tool confirmations execute with stale stream-time permissions

  • Category: Auth
  • Location: server/internal/domain/chat/agent.go:246
  • Description: After a write tool is approved, the agent executes it with the original SendMessage stream context. Confirmations can remain pending for minutes, while ResolveToolConfirmation only checks fleet:read on its fresh request before unblocking the old stream.
  • Impact: If an operator's miner/rack/schedule write permissions are revoked while a confirmation is pending but fleet:read remains, the pending approval can still complete a write using the permissions captured when the stream started.
  • Recommendation: Execute confirmed tools under a freshly authorized context, or make ResolveToolConfirmation perform the write after reloading effective permissions and checking the specific tool's required permission.

[MEDIUM] Rack slot write tools can leave partial state on failure

  • Category: Reliability
  • Location: server/internal/handlers/chat/tools.go:2123
  • Description: set_rack_slots clears requested devices first, then applies slot assignments one RPC at a time. clear_rack_slots similarly clears devices one by one. Any later RPC failure returns an error after earlier mutations have already committed.
  • Impact: An approved Minerbot action can fail while still clearing or partially changing rack layout state, leaving operators with a generic failure and a rack map that no longer matches the requested operation.
  • Recommendation: Add an atomic bulk rack-slot update/clear service method backed by one transaction, or implement explicit compensation and return a partial-success result that tells the operator exactly what changed.

[MEDIUM] Stored LLM credentials are usable by every fleet-read user

  • Category: Auth
  • Location: server/internal/handlers/chat/handler.go:96
  • Description: SendMessage requires only fleet:read, then loads the organization-scoped LLM runtime config, including the stored provider credential, and sends chat/tool context to the configured provider.
  • Impact: Any user with fleet:read can consume the organization's saved LLM API key, incur provider cost, and export fleet context available to their tools to a third-party model provider even if they cannot manage integrations or API keys.
  • Recommendation: Introduce an explicit chat/agent-use permission or policy gate for stored-provider usage, and add per-user/org rate limits and audit events for LLM calls.

Notes

No changed hunk showed direct SQL injection, shell command injection, protobuf wire-format breakage, hardcoded wallet/payout addresses, or mining-pool hijack behavior. The new pool-related tool returns pool names only and intentionally omits stratum URLs, usernames, wallet identifiers, and credentials.


Generated by Codex Security Review |
Triggered by: @mcharles-square |
Review workflow run

@mcharles-square
mcharles-square force-pushed the feat/ai-chat-poc branch 2 times, most recently from 96c4767 to 65b687e Compare July 17, 2026 20:37
@mcharles-square
mcharles-square force-pushed the feat/ai-chat-poc branch 2 times, most recently from 9274f7e to 0cc8901 Compare July 23, 2026 13:26
mcharles-square and others added 12 commits July 23, 2026 18:55
The new llm_config migration originally used version 000126, colliding
with 000126_add_curtailment_pending_dispatch_clock. main has since also
added 000127_backfill_curtailment_pending_dispatch_clock, so the next
free version against the PR merge base is 000128. Renumbering to 000128
keeps migrations sequential and gap-free and clears Migration Hygiene,
the DB-backed server tests (migration driver duplicate-version init
failure), and all ProtoFleet E2E shards. Migration contents unchanged.

Co-authored-by: Marvin Charles <mcharles@squareup.com>
Signed-off-by: Marvin Charles <mcharles@squareup.com>
Add write-capable chat agent tools with preview/execute pairs:
- preview_miner_action / execute_miner_action (explicit, all_devices, filter selectors)
- preview_downtime_window / create_downtime_window (maintenance schedules)
- list_actionable_miner_issues
Wire new services in main.go and update agent guidance + tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

client javascript Pull requests that update javascript code review-policy: needs-review Managed by the Review Policy workflow. server shared

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants