Skip to content

Configuration & Credentials

Most people use Discovery and Apply for feature-specific setup. This page owns the shared configuration boundary: effective-value precedence, local storage, LLM providers and model policy, the daily LLM spend ceiling, compensation-source policy, and advanced operator controls. A single ready Codex, Claude, or Google provider is sufficient for every core AI stage, including employer-analysis synthesis. A second provider can add ensemble diversity, but it is not mandatory.

JobCtrl configuration is intentionally local. Every value edited anywhere on /discovery lives in SQLite. Every non-secret desired value edited anywhere under /settings/** lives in config.json. Secrets entered through Settings remain in macOS Keychain or native provider stores; operator-supplied .env and launch-environment secrets stay outside the saved Settings document. Everything here is optional unless a feature you want depends on it.

Command spelling

Command blocks on this page use the canonical installed spelling, jobctrl <command>. The native executable is both the app launcher and the domain CLI from any directory after either curl or Homebrew acquisition. Contributors running from source can use the checkout-prefixed commands in Local Development.

Start Here

You want to change…Use
Candidate facts and resume evidenceProfile (/profile)
Work model, application, or writing preferencesPreferences (/preferences)
Target roles/locations, runtime, sources, schedules, quarantine, manual capture, or crawl identityDiscovery (/discovery)
Materials, application fields, approval modes, browser automation, or GmailApply
Spend/capacity, scoring guidance, or compensation source policySettings → General (/settings)
Provider secret or cloud modeSettings → Credentials (/settings/credentials) on macOS, or ~/.jobctrl/.env / the shell
Preferred provider model or employer-analysis perspectivesSettings → Model selection (/settings/models); see Employer Analysis Perspectives for how the selection is used during Discover preparation

The rest of this page is a shared-settings lookup. Data, Privacy & Safety explains what is stored or sent; Security explains the controls around risky actions.

How a setting becomes effective

For non-secret values editable in the UI, JobCtrl uses the first available value from the top of this hierarchy:

Effective-value resolution Read from highest to lowest: the first available value wins.Each source is checked only when the source above it has no value.

Resolution path

  1. 01HighestExplicit per-run valueUsed when the command or workflow supplies one. Command or workflow input

    No value supplied? Continue to the saved value.

  2. 02NextSaved UI valueUses the value you last saved in the relevant product surface. SQLite or config.json

    Still unset? Continue to the built-in default.

  3. 03FallbackBuilt-in defaultUsed only when neither of the higher-priority sources has a value. Packaged application default

Outside this hierarchy: non-secret environment variables are not a configuration layer.

Environment variables are not an alternate persistence store for non-secret UI settings. Secret loading is separate: a non-empty environment secret can take precedence over Keychain at process startup. Hard deny switches such as LANGFUSE_DISABLE=1 are also authoritative: they can force a corresponding feature off, but cannot turn it on.

SurfaceStorageAPIWhen a saved change applies
Settings → Generalconfig.json/v1/settingsLive, next poll/run/workflow, or restart, as labeled; worker activity slots show desired versus active values
Settings → CredentialsNon-secret desired values in config.json; secrets in macOS Keychain, the launch environment, or native provider stores/v1/credentialsClaude and Google Keychain edits require the relevant Python process to restart; an environment-owned active route remains authoritative until its value is removed and the process restarts; Codex verification is immediate
Settings → Model selectionconfig.json/v1/settings; /v1/providers/modelsNewly started work; no worker restart
Settings → Browser & extensionNon-secret capability choices and adopted executable configuration in config.json; pairing token and copied profile contents remain separate/v1/browser-capabilities; /v1/extension/pairing-tokenCapability changes and token rotation are live; making the authenticated LinkedIn browser ready immediately continues condition-blocked Enrich → Score → Tailor → Cover work without Discover

Discovery and Apply document the storage and activation timing for their feature-specific controls.

Configuration Sources

SourcePurpose
~/.jobctrl/jobctrl.dbCandidate profile, every field edited on /discovery, preferences, tailoring controls, jobs, events, projections, and artifact metadata.
~/.jobctrl/config.jsonEvery non-secret desired value edited under /settings/**, including budgets and capacity, application runtime, scoring guidance, model policy, browser capability choices, and compensation source policy. It never owns a field shown on /discovery.
~/.jobctrl/.envPersonal provider keys and runtime environment.
repo .envDevelopment-only overrides for the current checkout.
shell environmentOne-off overrides for commands and CI.
workers/automation/src/jobctrl/config/*.yamlPackaged employer and site behavior registries (employers.yaml, sites.yaml). The dynamic source registry lives in SQLite.

The development launcher loads ~/.jobctrl/.env, repo .env, and the optional JOBCTRL_USER_ENV_PATH file before starting local services.

On macOS, Settings → Credentials is the preferred guided provider setup. It stores Anthropic or Gemini API keys and selected provider-mode settings in macOS Keychain. Codex uses an authenticated Codex CLI, and AWS, Google, and Azure credentials stay in their native CLI-managed stores; JobCtrl records only the activation flags and non-secret identifiers needed to select those routes in config.json.

At Python process startup, after env-file loading, JobCtrl uses a Keychain value only when the corresponding environment value is missing or empty; any non-empty environment value wins. Saving or removing a value is therefore restart-to-activate for Python consumers: restart the relevant worker or provider process before Claude or Google work. Preferred-model changes do not require that restart.

Environment ownership is scoped to the active secret or auth route, not to the whole provider card. While an environment-owned route is active, its secret field and provider-removal control remain read-only, but Settings still lets you prepare another supported auth route. Saving that alternative does not override the active environment value. Remove the environment value and restart the relevant process before expecting the saved route to become effective.

The Credentials screen groups readiness, ownership, secret input, and actions as one responsive row for each supported route. At narrow widths those actions move below the field instead of squeezing it. Stored secret values are never rendered; the screen shows a human-readable configured/readiness state and a sanitized error when inspection fails. Settings section headers likewise use their product names and explanations rather than internal context tags or raw configuration keys as decorative metadata.

Native Windows and Linux credential-store adapters are planned; use .env or the shell on those platforms today. jobctrl doctor reports the effective source without printing secrets. Status unknown (inspection_failed) is distinct from not configured: it means JobCtrl could not inspect Keychain. Provider-mode replacement is all-or-nothing from the web contract; a failed change preserves the previous configuration or reports an explicit sanitized recovery failure.

Local Data

JobCtrl stores its local database, settings, provider environment file, generated artifacts, logs, and browser state under ~/.jobctrl by default. Most users should leave this location unchanged. Advanced users can set JOBCTRL_DIR before starting JobCtrl to relocate the entire local data directory. See Data, Privacy & Safety for what stays local and what may leave the machine. Contributors who need custom data paths, ports, Temporal settings, API/Vite proxy targets, or isolated stacks should use Local Development → Runtime Overrides.

LLM Providers

Choose one provider in Settings → Credentials, restart the relevant Python process after a Keychain edit, and use jobctrl doctor. The pipeline model spec defaults to default, which resolves through a ready provider. Explicit model specs use codex:, claude:, or google:; gemini: remains an alias for the Google SDK route.

After first setup or whenever provider authentication changes, run:

bash
jobctrl setup
jobctrl doctor

jobctrl setup detects existing authentication before prompting and writes only local configuration; it never commits or ships credentials. jobctrl doctor then reports which providers are ready without printing their secrets.

Model selection becomes available only after the corresponding provider is ready. Codex, Claude, and Google choices come from the same authenticated provider runtimes that execute the work: Codex App Server model/list, Claude Agent SDK initialization metadata, and the Google SDK model list. JobCtrl does not maintain a second hard-coded model registry. The picker shows the exact runtime model ID alongside a friendly name when they differ. Only provider and model IDs are written to config.json; credentials remain on the credential boundary. A model absent from the active runtime catalog is intentionally not selectable, even if that model exists in the provider's broader product lineup.

A saved preference is scoped to its provider and cannot change which provider JobCtrl selects. Newly constructed adapters use this precedence:

  1. explicit non-default workflow model;
  2. saved preference for the selected ready provider;
  3. that provider's default.

Provider selection itself keeps the default readiness order Claude, then Codex, then Google. Explicit provider:model workflow values select the named provider. Existing adapters and in-flight work keep their resolved model. Newly started work rechecks the effective selection when it acquires the shared adapter and receives a new adapter object when the saved preference changed; no worker restart is required for a preferred-model edit. The initially selected ready provider stays process-stable on warm acquisitions so status checks do not rerun for every workflow. Provider credential/readiness changes retain their existing restart requirement (or require an explicit adapter reset).

The Employer analysis perspectives selection is also saved here. Its execution behavior belongs to the Discover preparation workflow and is documented under Discovery → Employer Analysis Perspectives.

Codex

JobCtrl requires an already authenticated Codex CLI and reuses that authentication. Install Codex CLI and complete its supported sign-in flow before verifying it in JobCtrl.

Advanced operators can set JOBCTRL_CODEX_BIN to override the Codex runtime. By default, JobCtrl uses its pinned, bundled openai-codex-cli-bin binary.

Claude

Choose exactly one Claude Agent SDK route. Consumer Claude CLI login/OAuth does not count as provider readiness. The supported routes follow the official Agent SDK authentication guidance:

RouteSettings stored by JobCtrlCredential owned outside JobCtrl
Anthropic APIANTHROPIC_API_KEYNone
Google Vertex AICLAUDE_CODE_USE_VERTEX=1, ANTHROPIC_VERTEX_PROJECT_ID, CLOUD_ML_REGIONGoogle Application Default Credentials (gcloud auth application-default login) or GOOGLE_APPLICATION_CREDENTIALS naming an existing service-account JSON file
Amazon BedrockCLAUDE_CODE_USE_BEDROCK=1, optional AWS_PROFILE / AWS_REGIONAWS credential chain (Bedrock setup)
Claude Platform on AWSCLAUDE_CODE_USE_ANTHROPIC_AWS=1, ANTHROPIC_AWS_WORKSPACE_ID, optional AWS_PROFILE / AWS_REGIONAWS credential chain (Claude Platform on AWS)
Microsoft FoundryCLAUDE_CODE_USE_FOUNDRY=1, ANTHROPIC_FOUNDRY_RESOURCEAzure credential chain (az login; Foundry setup)

The Vertex route is documented separately in the official Claude on Vertex AI guide.

Google

Choose one:

RouteConfiguration
Gemini APIGEMINI_API_KEY (the runtime also accepts GOOGLE_API_KEY from the environment)
Vertex AIGOOGLE_GENAI_USE_VERTEXAI=1, GOOGLE_CLOUD_PROJECT, GOOGLE_CLOUD_LOCATION, plus valid Google Application Default Credentials

Vertex project and location values select the target; they are not credentials. If GOOGLE_APPLICATION_CREDENTIALS is set, it must name an existing regular, loadable service-account JSON file. The path is write-only and is not shown again. Otherwise JobCtrl checks the standard local gcloud ADC location, whose officially loadable ADC types (including authorized_user) remain supported.

LLM Spend Budget

Daily LLM budget

The daily LLM budget is stored in config.json and edited in Settings → General (dailyBudgetUsd, default 25; 0 means unlimited). Workflows that spend LLM tokens run a budget preflight before their heavy activities and stop with a non-retryable budget error once the estimated daily spend reaches the ceiling. GET /v1/health reports today's estimated spend against the configured budget.

Execution Concurrency

Settings separates pipeline parallelism from the worker capacity that executes it. Increasing either value does not create provider quota or bypass the daily budget.

Concurrent applications

Concurrent applications (applyConcurrency, default 1) limits how many application jobs the standing Apply loop may process at the same time. The loop re-reads the saved value from config.json on its next poll. It does not control Score, Tailor, Cover, or the number of Temporal activities the worker can run.

Pipeline internal concurrency

Pipeline internal concurrency (pipelineInternalConcurrency, default 1) is one saved value shared by manual Pipeline actions and automatic Score → Tailor → Cover preparation after a profile update. Changing the field on the Pipelines page saves the same config.json value shown under Settings → General; newly started batches use it. Existing in-flight workflows keep the value they started with.

This setting controls parallel work inside a pipeline batch. It does not create Temporal activity slots, and effective execution remains bounded by the active worker capacity.

Worker activity slots

Worker activity slots (workerActivitySlots, default 4) is the desired total Temporal activity capacity for the Python worker. A saved change requires a worker restart. The Settings screen shows both the desired value and the active value reported by the worker so pending restart state is explicit.

This capacity is distinct from Pipeline internal concurrency: activity slots bound how many activities can execute at once, while internal concurrency bounds parallel work inside an activity or batch. Temporal queues work above the active slot count.

Compensation Sources

Settings → General → Compensation sources stores the non-secret source and access-policy choices in config.json. Saving the setting changes policy only; it does not connect to a provider or fetch a feed.

Compensation Evidence owns the source modes, licensing boundaries, refresh behavior, provenance, normalization, and confidence rules. Provider payloads, restricted datasets, and credentials are not general settings and must never be committed.

Observability

VariableWhat it does
LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, LANGFUSE_BASE_URLEnable OpenTelemetry export of LLM, workflow, and JSON-RPC spans to Langfuse.
LANGFUSE_DISABLE=1Disable export even when credentials are present.
LANGFUSE_OTEL_TIMEOUT_SECONDSOTLP/HTTP export timeout, default 5.0.
JOBCTRL_ENVEnvironment attribute stamped on exported traces, default local.

When enabled, export is metadata-only: provider/model identifiers, operation or stage, success/failure, token counts, and safe size metrics. Raw prompts, messages, job text, profiles, generated materials, completions, credentials, local paths, logs, and database content are not span attributes.