Commit graph

271 commits

Author SHA1 Message Date
Snider
fb498f0b88 feat(api): canonical webhook events + chat completions transport discovery
Implements gaps between RFC.md spec and code:

- Export canonical webhook event identifiers (RFC §6) as Go constants:
  WebhookEventWorkspaceCreated, WebhookEventLinkClicked, etc. Plus
  WebhookEvents() and IsKnownWebhookEvent(name) helpers for SDK consumers
  and middleware validation.

- Surface the chat completions endpoint (RFC §11.1) through TransportConfig
  (ChatCompletionsEnabled + ChatCompletionsPath) and the OpenAPI spec
  extensions (x-chat-completions-enabled, x-chat-completions-path) so
  clients can auto-discover the local OpenAI-compatible endpoint.

- Add internal test coverage for chat completions sampling defaults
  (Gemma 4 calibrated temp=1.0, top_p=0.95, top_k=64, max_tokens=2048)
  and the ThinkingExtractor channel routing (RFC §11.6).

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-14 15:02:18 +01:00
Snider
da1839f730 feat(api): webhooks + sunset headers + WithWebSocket + cmd/api migration
- webhook.go: HMAC-SHA256 WebhookSigner matching PHP WebhookSignature —
  sign/verify, X-Webhook-Signature / X-Webhook-Timestamp headers,
  VerifyRequest middleware helper, 5-minute default tolerance,
  secret generator (RFC §6)
- sunset.go: ApiSunsetWith(date, replacement, opts...) + WithSunsetNoticeURL;
  ApiSunset now emits API-Suggested-Replacement when replacement set;
  RouteDescription.NoticeURL surfaces API-Deprecation-Notice-URL (RFC §8)
- options.go + api.go + transport.go: WithWebSocket(gin.HandlerFunc)
  alongside existing WithWSHandler(http.Handler); gin form wins when
  both supplied (RFC §2.2)
- openapi.go: apiSuggestedReplacement + apiDeprecationNoticeURL as
  reusable header components; NoticeURL on a RouteDescription flips
  operation deprecated flag and emits response header doc
- cmd/api/*.go: migrated from Cobra (cli.NewCommand, StringFlag) to
  new path-based CLI API (c.Command + core.Options.String/Int/Bool);
  replaces the 1,422-line Cobra test suite with _Good/_Bad/_Ugly
  triads on the new surface
- webhook_test.go + sunset_test.go + websocket_test.go: full coverage

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-14 14:51:04 +01:00
Snider
fbb58486c4 feat(api): WithChatCompletions option + bug fixes in chat_completions
- options.go: new WithChatCompletions(resolver) and
  WithChatCompletionsPath(path); api.New(...) now auto-mounts at
  /v1/chat/completions when a resolver is configured (previously the
  resolver could be attached but never mounted, which would have
  panicked Gin)
- chat_completions.go: fixed missing net/http import, dropped
  ModelType during discovery, Retry-After header set after c.JSON
  silently lost, swapped OpenAI error type/code fields, swapped
  validate call site, redundant nil check, builder length read before
  nil-receiver check
- openapi.go: effective*Path helpers surface an explicit path even
  when the corresponding Enabled flag is false so CLI callers still
  get x-*-path extensions; /swagger always in authentik public paths
- chat_completions_test.go: Good/Bad/Ugly coverage for new options,
  validation, no-resolver behaviour
- openapi_test.go: fix stale assertion for CacheEnabled-gated X-Cache
- go.mod: bump dappco.re/go/core/cli to v0.5.2
- Removed local go-io / go-log stubs — replace points to outer
  modules for single source of truth
- Migrated forge.lthn.ai/core/cli imports to dappco.re/go/core/cli
  across cmd/api/*.go + docs

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-14 14:34:51 +01:00
Snider
996b5a801a feat(api): chat completions endpoint per RFC §11
- chat_completions.go: ChatCompletionRequest/Response/Chunk types,
  POST /v1/chat/completions handler with SSE streaming, ModelResolver,
  ThinkingExtractor, calibrated defaults, OpenAI-compatible error shape
- api.go: wires the chat endpoint into the gateway

From codex spark-medium pass, 851 lines.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-14 14:00:15 +01:00
Snider
d90a5be936 refactor: AX compliance sweep — replace banned stdlib imports with core primitives
Replaced fmt, strings, sort, os, io, sync, encoding/json, path/filepath,
errors, log, reflect with core.Sprintf, core.E, core.Contains, core.Trim,
core.Split, core.Join, core.JoinPath, slices.Sort, c.Fs(), c.Lock(),
core.JSONMarshal, core.ReadAll and other CoreGO v0.8.0 primitives.

Framework boundary exceptions preserved where stdlib types are required
by external interfaces (Gin, net/http, CGo, Wails, bubbletea).

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-13 09:32:00 +01:00
Snider
7bcb6d469c fix(pr#2): address CodeRabbit round 4 — newCacheStore fail-closed for unbounded cache
Prevent silent unbounded cache creation when both maxEntries and maxBytes
are non-positive: newCacheStore now returns nil, WithCacheLimits skips
middleware registration, and WithCache defaults to 1 000-entry LRU cap
when called with only a TTL argument.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-07 11:32:28 +01:00
Snider
e27006de30 fix(pr#2): address CodeRabbit round 3 review findings
- cache: fix LRU race — check expiry before MoveToFront within same lock
  critical section; previously a stale entry could be promoted before
  expiry was verified
- cache: drop stale ETag/Content-Md5/Digest headers when body is
  rewritten by refreshCachedResponseMeta to avoid misrepresenting the
  new body to clients
- ratelimit: cap buckets map at 100k entries with stale-eviction fallback
  and shared overflow bucket to prevent unbounded memory growth under
  high-cardinality traffic
- ratelimit: fix clientRateLimitKey comment — credentials are tried
  second (before IP), not as a "last resort"

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-07 09:29:03 +01:00
Snider
372326297e fix(pr#2): address CodeRabbit round 2 review findings
Go:
- cache: fix TOCTOU race in get() — re-verify entry pointer under lock before
  evicting to prevent corrupting s.currentBytes and removing a newly-set entry
- bridge: fix writeErrorResponse recorder out of sync — buffer into w.body/
  w.headers and call commit() so Status(), Header(), Size() reflect error response
- bridge: fix ValidateResponse number precision — use json.Decoder+UseNumber for
  initial envelope decode to preserve large integers (matches Validate path)
- ratelimit: fix unreachable credential branches — move X-API-Key and
  Authorization hashing before IP fallback so NAT'd clients are bucketed by key
- openapi: gate cacheSuccessHeaders on sb.CacheEnabled flag, not just method==get
- openapi: use isNilRouteGroup in prepareRouteGroups to catch typed-nil RouteGroup

PHP:
- RateLimitExceededException: remove ad-hoc CORS handling — let framework CORS
  middleware apply correct headers for all responses including errors
- SeoReportService.extractCharset: parse charset token from Content-Type value
  instead of returning the full "text/html; charset=utf-8" string
- SeoReportService: validate IP literals directly with filter_var before DNS
  lookup so ::ffff:127.0.0.1-style hosts don't bypass private-IP checks
- SeoReportService.isPrivateIp: extract isPrivateIpv4 helper; handle
  IPv4-mapped IPv6 (::ffff::/96) by checking embedded IPv4 against private
  ranges; add 0.0.0.0/8 to private range list

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-07 09:11:05 +01:00
Snider
e54dd2e370 fix(pr#2): address CodeRabbit major/critical review findings
Go:
- codegen: pass trimmed specPath to buildArgs instead of raw g.SpecPath
- cmd/sdk: use local resolvedSpecFile to avoid mutating flag variable per-invocation
- export: write to temp file + atomic rename to prevent destination truncation on failure
- openapi: gate effectiveGraphQLPath/SwaggerPath/WSPath/SSEPath on enable flags; use effectiveSwaggerPath in effectiveAuthentikPublicPaths
- cache: reject oversized replacement before mutating LRU state for existing keys
- ratelimit: move setRateLimitHeaders before c.Next() so headers are sent; hash credential headers with SHA-256 to avoid storing raw secrets; prefer validated principal from context
- response_meta: track size separately from body buffer so Size() is accurate after body rewrites and in passthrough mode
- bridge: limit request body reads with http.MaxBytesReader (10 MiB); allow missing data key in ValidateResponse for nil/zero success responses; update recorder status in writeErrorResponse
- pkg/provider/proxy: validate target scheme and host after url.Parse to catch hostless inputs
- cmd_test: snapshot/restore global spec registry in TestAPISpecCmd_Good_RegisteredSpecGroups

PHP:
- HasApiResponses.php, config.php: add declare(strict_types=1)
- RateLimitExceededException: validate Origin against cors.allowed_origins before reflecting in CORS header
- ApiUsageService: import and use Core\Api\Models\ApiKey instead of fully-qualified Mod\ path
- SeoReportService: add SSRF protection (scheme check, private-IP rejection); add .throw() for HTTP error handling; disable automatic redirects

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-07 08:38:41 +01:00
Snider
194e7f61df fix: migrate module paths from forge.lthn.ai to dappco.re
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-04 16:21:11 +01:00
Virgil
aea902ed28 fix(cmd/api): forward graphql playground path to sdk specs
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-03 05:02:47 +00:00
Virgil
8dd15251ea fix(api): omit disabled graphql playground spec metadata
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-03 04:56:48 +00:00
Virgil
a3a1c20e7a fix(api): support custom GraphQL playground paths
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-03 04:53:30 +00:00
Virgil
3896896090 fix(api): correct OpenAPI iterator examples
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-03 04:50:54 +00:00
Virgil
0ec5f20bf5 fix(api): add AX examples to client snapshots 2026-04-03 04:45:03 +00:00
Virgil
8b5e572d1c fix(api): expose OpenAPI client snapshots
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-03 04:42:14 +00:00
Virgil
76acb4534b fix(api): surface GraphQL playground metadata
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-03 04:38:22 +00:00
Virgil
1491e16f9e fix(api): normalise runtime metadata snapshots
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 16:48:37 +00:00
Virgil
0022931eff fix(openapi): normalise spec builder metadata
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 14:48:26 +00:00
Virgil
2b71c78c33 fix(openapi): ignore non-positive cache ttl in spec
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 14:42:01 +00:00
Virgil
be43aa3d72 fix(openapi): deep clone route metadata
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 14:35:59 +00:00
Virgil
2d09cc5d28 fix(api): add tracing AX examples
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 14:29:59 +00:00
Virgil
5971951c87 fix(cmd/api): trim spec metadata inputs 2026-04-02 14:25:59 +00:00
Virgil
d7290c55ec fix(cmd/api): align cache metadata with runtime
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 14:22:32 +00:00
Virgil
8301d4d1c7 fix(cmd/api): ignore non-positive cache ttl in spec
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 14:18:31 +00:00
Virgil
579b27d84e refactor(openapi): precompute authentik public paths
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 14:09:35 +00:00
Virgil
eb771875e2 fix(openapi): document authentik public paths as public
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 14:03:16 +00:00
Virgil
0dc9695b91 feat(api): include graphql in runtime snapshots 2026-04-02 13:58:56 +00:00
Virgil
0a299b79c1 fix(api): normalise empty Authentik public paths
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 13:55:17 +00:00
Virgil
a6693e1656 feat(api): surface effective Authentik public paths in specs
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 13:51:54 +00:00
Virgil
a07896d88e fix(cmd/api): normalise authentik spec public paths
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 13:46:45 +00:00
Virgil
bfef7237cc fix(api): harden SDK generator inputs
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 13:43:07 +00:00
Virgil
f6add24177 fix(api): normalise authentik public paths
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 13:38:56 +00:00
Virgil
f234fcba5f feat(api): surface authentik metadata in specs
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 13:25:23 +00:00
Virgil
eb18611dc1 feat(api): snapshot authentik runtime config
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 13:17:08 +00:00
Virgil
0171f9ad49 refactor(api): assert swagger spec interface
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 13:12:08 +00:00
Virgil
ec268c8100 feat(api): default enabled transport paths in specs
Treat enabled built-in transports as having their default paths when callers omit an explicit override. This keeps manual SpecBuilder usage aligned with the engine defaults and prevents Swagger, GraphQL, WebSocket, and SSE metadata from disappearing from generated documents.\n\nCo-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 13:07:05 +00:00
Virgil
ef51d9b1c3 refactor(cmd/api): centralize spec flag binding
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 13:01:45 +00:00
Virgil
ede71e2b1f feat(cmd/api): infer spec transport enablement from flags
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 12:56:59 +00:00
Virgil
655faa1c31 refactor(api): add runtime config snapshot
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 12:52:06 +00:00
Virgil
3c2f5512a8 feat(api): add GraphQL config snapshot
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 12:47:42 +00:00
Virgil
814c1b6233 feat(cmd/api): expose cache and i18n spec flags
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 12:43:06 +00:00
Virgil
5c067b3dae refactor(api): normalise config snapshots
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 12:38:54 +00:00
Virgil
f919e8a3be feat(api): expose cache and i18n OpenAPI metadata
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 12:29:40 +00:00
Virgil
5de64a0a75 feat(api): add i18n config snapshot
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 12:25:38 +00:00
Virgil
f760ab6c72 feat(api): expose cache config snapshot
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 09:23:02 +00:00
Virgil
592cdd302e fix(api): fail fast on sdk generator availability
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 09:19:19 +00:00
Virgil
c4743a527e refactor(cmd/api): fail fast on sdk generator availability
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 09:15:03 +00:00
Virgil
78d16a75cc docs(api): add AX example for SDK availability check
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 09:12:00 +00:00
Virgil
71c179018d refactor(api): snapshot route metadata during spec build
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 09:08:16 +00:00