Skip to content

13 auth token - #20

Merged
SaifRehman merged 17 commits into
mainfrom
13_auth_token
Apr 27, 2026
Merged

13 auth token#20
SaifRehman merged 17 commits into
mainfrom
13_auth_token

Conversation

@SaifRehman

Copy link
Copy Markdown
Contributor

No description provided.

SaifRehman and others added 17 commits April 27, 2026 22:59
…r, per-IP rate limiter (bounded + ctx-bound janitor), startup guard,

  agentfm_auth_attempts_total metric. 22 unit tests. Not wired into routes yet (PR 2).
- StartAPIServer(bind, port) — refuses non-loopback bind without API keys
- AGENTFM_ALLOW_UNAUTH_PUBLIC=1 escape hatch
- help.go: -api-bind row, two new examples, env-vars table
- 1 new integration test (refusal); CLI smoke verified

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- api_key constructor arg (sync + async); AGENTFM_API_KEY env fallback
- AuthenticationError; maps unauthorized + invalid_api_key codes
- Authorization: Bearer header on every request when set
- with_options inherits api_key
- 15 new unit tests; 164/164 green

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Constructor + with_options use _Unset sentinel: omit -> env fallback,
  None -> explicit no-auth, str -> use literal
- 5 new with_options tests; CHANGELOG entry under Unreleased
- 169/169 SDK tests green; ruff + mypy clean

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Root README: ## Authentication with token gen, startup-refusal,
  per-IP rate limiting, SDK + OpenAI examples
- Refreshed OpenAI drop-in snippet to show real api_key usage
- Caveats: auth bullet flipped to "implemented"
- Security Model table: HTTP gateway auth row added
- CLI Reference: -api-bind row added
- Python SDK README: brief Authentication subsection

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- C2: handleModels releases RLock before json.Encode to a slow client
  (avoids starving telemetry pulse on RWMutex writer-preferring semantics)
- C3: discoverPeers checks ctx.Err() per-iteration of the FindPeers
  channel range, bounds shutdown to a single dial deadline
- C4: artifact taskID requires leading alphanum + [a-zA-Z0-9_.-]{0,127};
  rejects zero-length idLen entirely; new regression test

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- I5: replace time.Now().UnixNano() task IDs with newCompletionID
  ("task_<24hex>") at api_async.go, api_handlers.go, execute.go,
  worker/sandbox.go (sessionID — concurrent containers no longer race
  for /tmp/output bind-mount)
- I7: bufio.Scanner cap raised from 1 MiB to 8 MiB via maxLineBytes
  const; LLM agents emitting structured-output JSON without newlines
  routinely exceed 1 MiB

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- I18: runWorkerMode wraps with signal.NotifyContext so metrics.Serve
  and worker.Start observe SIGINT/SIGTERM (was bound to bare ctx that
  never cancelled — metrics server was reaped only by process exit)
- I19: cmd/relay/main.go replaces bespoke signal.Notify channel with
  signal.NotifyContext; eliminates dual shutdown-handler smell

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…y tests

- Py I3: stacklevel=4 -> 3 via ROUTING_WARNING_STACKLEVEL constant;
  warning now attributes to the user's create() call line
- Py I4: extract raise_translated_stream_error() helper in _transport;
  6 streaming sites (3 sync + 3 async) collapse to one-line dispatch;
  httpx.UnsupportedProtocol now mapped to InvalidRequestError on
  streaming endpoints (was wrongly mapped to WorkerStreamError)
- Sync<->async parity: AsyncAgentFMClient.tasks.scatter_by_model gains
  pick=Callable kwarg matching the sync side
- Tests: tests/unit/test_parity.py introspects sync vs async namespaces
  and asserts matching method names + matching kwargs (would have
  caught the historical pick= drift); test_sync_client.py adds
  attribution test for ROUTING_WARNING_STACKLEVEL
- 174/174 SDK tests green; ruff + mypy clean

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Worker now prints a WARN-level startup banner about the host-network
threat model: agent containers can reach 127.0.0.1 services (Ollama,
admin endpoints, cloud metadata). Defaulting to --network none would
break the entire LLM-on-loopback workflow ecosystem; doc warning is
the right stop-gap until a phased rollout (issue follow-up tracked
in plans/agentfm-full-audit-2026-04-27.md PR-A5 section).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- I15: Boss.asyncSlots channel caps in-flight /api/execute/async at
  MaxInflightAsyncTasks (256); non-blocking acquire returns 503 with
  Retry-After + OpenAI envelope (code=async_capacity_exhausted) when
  saturated; goroutine releases the slot on exit
- I17: pruneDisconnectedWorkers extracted into a 30s ticker inside
  listenTelemetry; handleGetWorkers becomes a pure RLock + read; no
  side effects on a GET request
- I6: replace fixed time.After(2s) artifact wait with waitForArtifact()
  poll (100ms cadence, 10s deadline); responsive when the file
  appears earlier, bounded when it doesn't

Tests: pruneDisconnectedWorkers_EvictsDeadPeer, handleGetWorkers_PureRead,
asyncExecuteHandler_CapacityExhausted; 7/7 packages green under -race.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ay filter)

- I3: loadOrGenerateIdentity uses slog.Warn (not fmt.Printf) for
  corrupt-key + persist-failure paths so JSON-log aggregators see them
- I13: LoadSwarmKey os.Stat checks file mode and warns if it is
  group/world readable; spec is 0600
- I14: isDirEmpty conservative-default behavior documented inline
- I20: writeSSEDone surfaces write errors as a slog.Debug crumb instead
  of silent _, _ =
- I10: dialWorkerStream skips the circuit-relay address when the relay
  is not currently connected; saves StreamDialTimeout per task
  against a dead lighthouse
- MeshNode now caches RelayPeerID at Setup so the connectedness check
  doesn't have to re-parse the multiaddr per dial

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…_, sentinel test)

- Py I1: WebhookReceiver._parse_content_length enforces max_body_bytes
  internally; cap is now in one place so a future chunked-transfer
  refactor can't accidentally bypass the size guard
- Py I5: AgentFMClient + AsyncAgentFMClient grow defensive __del__ so
  a client built via with_options(...) outside a `with` block doesn't
  leak the underlying httpx.Client until interpreter exit
- Py I13: SentinelFilter regression test for partial-sentinel input
  (truncated transport that opens "[AGENTFM:" but never closes it)

175/175 SDK tests green; ruff + mypy clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- WorkerProfile: drop redundant alias="name" (field already named "name")
- isLoopbackBind: drop misleading "[::1]" literal switch case (net.ParseIP
  rejects bracketed form anyway; loopback IPv6 is matched via ParseIP+
  IsLoopback below)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…uidance)

- Security Model table: container --network host caveat made explicit
  (loopback + cloud-metadata + Ollama exposure)
- Authentication section: reverse-proxy rate-limiter caveat documented;
  async submission cap (MaxInflightAsyncTasks=256) called out;
  operator-monitoring note (alert on agentfm_auth_attempts_total
  metrics, not log scrapes)

Audit doc at ~/.claude/plans/agentfm-full-audit-2026-04-27.md updated
with execution log + deferred-items rationale + follow-up issues list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@SaifRehman
SaifRehman merged commit a4958c3 into main Apr 27, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant