Model sync:
- Filter OpenRouter models by what the user's AI client can actually access,
before upserting metadata (avoids bloating with inaccessible models).
- Fall back to direct endpoint sync when no OpenRouter metadata matches
(handles Bailian/MiniMax and other non-OpenRouter providers).
Git stability fixes:
- SSH: add 5s timeout on stdin flush/shutdown in channel_eof and
cleanup_channel to prevent blocking the event loop on unresponsive git.
- SSH: remove dbg!() calls from production code paths.
- HTTP auth: pass proper Logger to SshAuthService instead of discarding
all auth events to slog::Discard.
Dependencies:
- reqwest: add native-tls feature for HTTPS on Windows/Linux/macOS.
All log calls in sync.rs now use slog macros:
- sync_once: uses logger passed as parameter (sourced from AppService.logs)
- sync_upstream_models (HTTP API): uses self.logs
- Remove use of tracing::warn/info/error entirely
The periodic background sync task and the HTTP API handler now
write to the same slog logger as the rest of the application.
Drop all hard-coded model-name lookup tables that hardcoded specific
model names and prices:
- infer_context_length: remove GPT-4o/o1/Claude/etc. fallback table
- infer_max_output: remove GPT-4o/o1/etc. output token limits
- infer_pricing_fallback: remove entire hardcoded pricing table
- infer_capability_list: derive from architecture.modality only,
no longer uses model name strings
Also fix stats: if upsert_version fails, skip counting and continue
rather than counting model but not version (which caused
versions_created=0 while pricing_created>0 inconsistency).
SSH is deployed inside Kubernetes cluster where rate limiting
at the application layer is unnecessary. Remove all SSH rate
limiter code:
- SshRateLimiter from SSHandle and SSHServer structs
- is_user_allowed checks in auth_publickey, auth_publickey_offered
- is_repo_access_allowed in exec_request
- is_ip_allowed in server::new_client
- rate_limiter module and start_cleanup
- Add `start_sync_task()` in agent/sync.rs: spawns a background task
that syncs immediately on app startup, then every 10 minutes.
- `sync_once()` performs a single pass; errors are logged and swallowed
so the periodic task never stops.
- Remove authentication requirement from OpenRouter API (no API key needed).
- Call `service.start_sync_task()` from main.rs after AppService init.
- Also update the existing `sync_upstream_models` (HTTP API) to remove
the now-unnecessary API key requirement for consistency.
Fail fast if repo storage_path does not exist on disk, before
blocking the thread pool with spawn_blocking. This prevents
invalid tasks from consuming blocking threads while failing.
- Fix match pattern: `Ok(Ok(Err(_)))` must be treated as NOT a panic,
since execute_task_body returns Err(()) on task failure (not a panic).
Previously the Err path incorrectly set panicked=true, which could
cause pool to appear unhealthy.
- Add "task failed" log before retry/discard decision so real error
is visible in logs (previously only last_error on exhausted-retries
path was logged, which required hitting 5 retries to see the cause).
- Convert 3 remaining pool/mod.rs shorthand logs to format!() pattern.
- Add retry_count to HookTask with serde(default) for backwards compat
- Limit hook task retries to MAX_RETRIES=5, discard after limit to prevent
infinite requeue loops that caused 'task nack'd and requeued' log spam
- Add nak_with_retry() in RedisConsumer to requeue with incremented count
- Standardize all slog logs: replace "info!(l, "msg"; "k" => v)" shorthand
with "info!(l, "{}", format!("msg k={}", v))" across ssh/authz.rs,
ssh/handle.rs, ssh/server.rs, hook/webhook_dispatch.rs, hook/pool/mod.rs
- Downgrade russh from 0.55.0 to 0.50.4
- Remove unused flate2 feature from russh dependency
- Use info!(logger, "{}", format!(...)) for channel lifecycle log messages
- Remove user=="git" restriction from auth_password: the actual user is
determined by the token, not the SSH username, matching Gitea's approach
- Add channel_open_session logging with explicit flush to verify
CHANNEL_OPEN_CONFIRMATION reaches the client
- Add pty_request handler (reject with log) so git clients that request
a PTY are handled gracefully instead of falling through to default
- Add subsystem_request handler (log + accept) so git subsystems are
visible in logs
- Prefix unused variables with _ to eliminate warnings
- Add LFS_MAX_OBJECT_SIZE (50 GiB) and validate object sizes in both the
batch advisory check and the upload_object streaming loop to prevent
unbounded disk usage from malicious clients
- Fix HTTP rate limiter: track read_count and write_count separately so
a burst of writes cannot exhaust the read budget (previously all
operations incremented read_count regardless of type)
HTTP:
- Return Err(...) instead of Ok(HttpResponse::...) for error cases so
actix returns correct HTTP status codes instead of 200
- Add 30s timeout on info_refs and handle_git_rpc git subprocess calls
- Add 1MB pre-PACK limit to prevent memory exhaustion on receive-pack
- Enforce branch protection rules (forbid push/force-push/deletion/tag)
- Simplify graceful shutdown (remove manual signal handling)
SSH:
- Fix build_git_command: use block match arms so chained .arg() calls
are on the Command, not the match expression's () result
- Add MAX_RETRIES=5 to forward() data-pump loop to prevent infinite
spin on persistent network failures
- Fall back to raw path if canonicalize() fails instead of panicking
- Add platform-specific git config paths (/dev/null on unix, NUL on win)
- Start rate limiter cleanup background task so HashMap doesn't grow
unbounded over time
- Derive Clone on RateLimiter so SshRateLimiter::start_cleanup works
Backend:
- Atomic seq assignment via Redis Lua script: INCR + GET run atomically
inside a Lua script, preventing duplicate seqs under concurrent requests.
DB reconciliation only triggers on cross-server handoff (rare path).
- Broadcast channel capacity: 10,000 → 100,000 to prevent message drops
under high-throughput rooms.
Frontend:
- Optimistic sendMessage: adds message to UI immediately (marked
isOptimistic=true) so user sees it instantly. Replaces with
server-confirmed message on success, marks as isOptimisticError on
failure. Fire-and-forget to IndexedDB for persistence.
- Seq-based dedup in onRoomMessage: replaces optimistic message by
matching seq, preventing duplicates when WS arrives before REST confirm.
- Reconnect jitter: replaced deterministic backoff with full jitter
(random within backoff window), preventing thundering herd on server
restart.
- Visual WS status dot in room header: green=connected, amber
(pulsing)=connecting, red=error/disconnected.
- isPending check extended to cover both old 'temp-' prefix and new
isOptimistic flag, showing 'Sending...' / 'Failed' badges.
validate_origin() only allowed localhost origins by default, causing
production WebSocket connections to be rejected. Now it reads
APP_DOMAIN_URL and APP_STATIC_DOMAIN from env and automatically
adds their http/https/ws/wss variants to the allowed origins list.
Also add APP_DOMAIN_URL to the production configmap.
The frontend WebSocket client sends room_public, but the HTTP fallback
sends it as a JSON body parsed directly into RoomCreateRequest/RoomUpdateRequest
which expects public. Add #[serde(rename = "room_public")] so both
ws params and HTTP JSON body work consistently.
Add ssh_clone_url and https_clone_url to ProjectRepositoryItem,
constructed from config.ssh_domain() and config.git_http_domain().
Update frontend RepoInfo interface and header.tsx to use the
server-provided URLs instead of hardcoded values.
The previous single-quote syntax with escaped quotes was split by
split_sql_statements on semicolons inside the function body.
Use $$ quoting to avoid quote escaping issues.
m20260411_000003_add_workspace_id_to_project was running before
m20250628_000013_create_project, causing "relation project does not exist".
Move all project table CREATEs before workspace migrations.
Foreign keys at the database level cause issues with deployment flexibility.
Keep only indexes for query performance; enforce referential integrity at
the application level.