candidrail
Structured hiring pipeline services for managing the hiring process end to end.
- Backend written in Rust, split accros various crates in ./crates
- UI written in Rust with Dioxus, located in ./ui
Candidate CRM
Candidate search uses stable tenant-scoped cursors and bounded summaries rather than loading and joining every candidate and application in the browser. Recruiters can filter by ownership, archive state, tags, talent pools, source, application state, custom data, and activity dates. Profiles retain normalized contact lookup values alongside the entered display value and source history. Exact duplicate review explains matching email/phone signals; merges require explicit conflict choices, an idempotency key, and an audit reason.
Candidate detail includes owner and do-not-contact state, document lifecycle controls, tags, pools, source history, duplicate review, privacy facts, and archive/restore. Document storage keys and checksums remain internal. Downloads are unavailable until type validation and quarantine promotion complete. Resume-derived suggestions are versioned against the source object and cannot update a profile until a recruiter reviews the proposed fields.
Recruiters can save private or team-visible filter views, select candidates for previewed bulk changes, and assign owners, collaborators, and follow-up tasks from named active teammates. CRM configuration supports reversible tag, pool, and typed custom-field administration. Candidate activity combines available notes, application changes, source/document/import events, tasks, and audited mutations in a bounded timeline. Reviewed employment, education, skill, and link facts keep their source document or derivative provenance and remain proposals until explicitly approved.
CSV imports are persisted as bounded per-row work with dry-run diagnostics and replay-safe job
execution. Use ccli candidates --help for preview/import/status/resume/export commands. Candidate
exports require the named export capability and apply contact and custom-field visibility rules.
Search, reporting, and analytics
Organization workspaces provide permission-aware global search across candidates, positions, and
applications, using stable opaque cursors and bounded sparse results. Dashboards, drill-downs,
reports, CSV exports, and scheduled safe-link delivery share the event-derived metric definitions in
docs/analytics-metrics.md. Results always identify timezone, date
range, definition version, data watermark, and suppression state. Demographic reporting is not
available without a separately reviewed data and authorization model.
Communications and collaboration
Organization members have a permission-filtered activity timeline, typed and revisioned notes, mentions, watchers, assigned-task queues, and an in-app notification center. Candidate conversations support versioned localized templates, preview, durable immediate or scheduled delivery, cancellation, delivery history, and privacy export. Channel preferences, quiet hours, digests, sender identities, suppressions, provider health, and audited dead-letter replay are available from communication settings; mandatory security notifications cannot be disabled.
Mail delivery is provider-neutral. Development uses deterministic in-memory or private file sinks;
production uses the configured HTTPS gateway from the durable worker. Outbox payloads are bounded
references rather than rendered mail or candidate content, and provider events are signature-
verified, idempotent, and monotonic. One-time action secrets use authenticated, outbox-bound
envelopes whose ciphertext is erased after provider-idempotent delivery. See OPERATIONS.md for
worker and mail configuration.
AI candidate screening
Candidate applications can be screened from the candidate and position views. Each attempt stores the model provider, model, web-research implementation, compact source/query provenance, prompt version, custom instructions, input snapshot, result, status, and timestamps so previous screenings remain auditable. The assessment is decision support only and does not make a hire or reject recommendation.
The server uses the provider-independent Rig agent library. Configure it with server flags or their equivalent environment variables:
| Flag | Environment variable | Default | Notes |
|---|---|---|---|
--ai-provider | AI_PROVIDER | openai | openai, anthropic, gemini, openrouter, or openai-compatible |
--ai-model | AI_MODEL | gpt-5-mini | Provider model identifier |
--ai-api-key | AI_API_KEY | unset | Required to run a screening |
--ai-base-url | AI_BASE_URL | unset | Optional provider endpoint override; required for openai-compatible |
The server can start without an AI API key; screening attempts then fail with a clear, safe configuration message while the rest of the application remains available. Candidrail selects the web-research route from the configured provider, endpoint, and model:
| Configuration | Web research |
|---|---|
OpenAI with no base-URL override and GPT-4.1 or GPT-4.1 mini (including dated variants), a standard gpt-5* model (excluding Search API and Codex variants), o3, o3-pro, or o4-mini | OpenAI native web search |
| Anthropic with no base-URL override and Claude 3.5 Haiku, Claude 3.7 Sonnet, Claude 4+ Opus/Sonnet/Haiku, Fable 5, or Mythos model families | Anthropic native web search |
| Gemini, OpenRouter, OpenAI-compatible or custom endpoints, and other models | Built-in DuckDuckGo search and direct page fetches |
Provider-native research uses the configured AI provider credentials. Built-in research needs no separate web-search account or API key: Candidrail sends role-related queries to DuckDuckGo and fetches selected public pages directly. Candidate data and custom instructions are never included in web-research requests; research receives position information only. Public sources provide role context, not candidate evidence, and compact provenance is stored so citations remain auditable. Built-in research requires outbound HTTPS access to DuckDuckGo and outbound HTTP(S) access to selected public sources. Native research failures do not silently switch to the built-in route.
For example:
AI_PROVIDER=openai AI_MODEL=gpt-5-mini AI_API_KEY=... cargo run -p ccli -- server