Repository navigation
Expand file tree
/
Copy path.env.example
More file actions
187 lines (167 loc) · 9.71 KB
/
Copy path.env.example
File metadata and controls
187 lines (167 loc) · 9.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# HTTP server port
PORT=8080
# Postgres connection string
DATABASE_URL=postgres://hire:hire@localhost:5432/hire?sslmode=disable
# Browser origin allowed to call the API cross-origin (the web frontend).
# Also where OAuth callbacks send the browser back to, and the base of the
# per-provider callback URL: <origin>/api/v1/auth/oauth/<provider>/callback
FRONTEND_ORIGIN=http://localhost:5173
# Auth: JWT signing secret (required — the server fails fast if unset or under
# 32 bytes) and session TTL. Set a strong random JWT_SECRET in any non-local deployment.
JWT_SECRET=dev-insecure-secret-change-me-0123456789
# JWT_TTL=24h
# Mark the session cookie Secure (set true on any HTTPS deployment)
# COOKIE_SECURE=false
# OAuth sign-in (all optional — a provider with empty credentials is disabled).
# Register the callback URL at each provider's developer console:
# <FRONTEND_ORIGIN>/api/v1/auth/oauth/<provider>/callback
# Google: https://console.cloud.google.com/apis/credentials
OAUTH_GOOGLE_CLIENT_ID=
OAUTH_GOOGLE_CLIENT_SECRET=
# --- OJCP manifest signing (optional) -----------------------------------------
# Path to a PEM P-256 private key that signs /.well-known/ojcp.json; its public half is
# published at /.well-known/ojcp-keys.json. Unset = unsigned manifest, no key set.
# Generate: openssl ecparam -name prime256v1 -genkey -noout -out ojcp-signing.pem
# OJCP_SIGNING_KEY_FILE=/opt/freehire/ojcp-signing.pem
# --- Connect-Gmail inbox (optional) ------------------------------------------
# The "Connect Gmail" ATS inbox reuses the Google OAuth client above, requesting
# the RESTRICTED scope gmail.readonly via incremental authorization on connect.
# Google Cloud setup: enable the Gmail API, add the gmail.readonly scope to the
# OAuth consent screen, register <FRONTEND_ORIGIN>/api/v1/me/gmail/callback as a
# redirect URI, and (until app verification) add your account under "Test users".
# GMAIL_TOKEN_KEY is a base64-encoded 32-byte AES key that encrypts stored refresh
# tokens at rest; unset (or not 32 bytes) disables the feature. Generate one with:
# openssl rand -base64 32
GMAIL_TOKEN_KEY=
# Hosted mailbox (the second inbox option): each user can claim an address on a
# receiving subdomain and read mail sent there. MAILBOX_DOMAIN is that subdomain
# (e.g. inbox.freehire.me — NOT the MAIL_DOMAIN sending identity); unset disables
# the option (claim route off, status
# unavailable). The cmd/mail-ingest daemon drains inbound mail from SES → S3 →
# SQS: set AWS_REGION (an SES inbound-capable region), MAIL_INBOUND_QUEUE_URL,
# and MAIL_INBOUND_BUCKET (fallback bucket when a notification omits one). AWS
# credentials come from the default chain (instance/SSO role), never here.
MAILBOX_DOMAIN=
AWS_REGION=
MAIL_INBOUND_QUEUE_URL=
MAIL_INBOUND_BUCKET=
# GitHub: https://github.com/settings/developers (OAuth Apps)
OAUTH_GITHUB_CLIENT_ID=
OAUTH_GITHUB_CLIENT_SECRET=
# LinkedIn: https://www.linkedin.com/developers/apps — the app must have the
# "Sign In with LinkedIn using OpenID Connect" product enabled
OAUTH_LINKEDIN_CLIENT_ID=
OAUTH_LINKEDIN_CLIENT_SECRET=
# Apple: developer.apple.com/account — register a Services ID (OAUTH_APPLE_CLIENT_ID)
# under Certificates, IDs & Profiles, associate it with the app's App ID, and enable
# Sign In with Apple with this callback as its return URL. No static client secret —
# freehire signs one itself per token exchange from a Team ID + Key ID + private key
# (a "Sign in with Apple" key created under Keys). OAUTH_APPLE_PRIVATE_KEY is the
# key's .p8 contents, base64-encoded (a multi-line PEM does not survive a systemd
# EnvironmentFile reliably — same convention as GMAIL_TOKEN_KEY):
# base64 -i AuthKey_XXXXXXXXXX.p8
OAUTH_APPLE_CLIENT_ID=
OAUTH_APPLE_TEAM_ID=
OAUTH_APPLE_KEY_ID=
OAUTH_APPLE_PRIVATE_KEY=
# Résumé storage (all optional — storage is enabled only when all four are set;
# otherwise résumé upload just extracts skills in-request, no regression).
# Provider-agnostic: any S3-compatible endpoint works (Hetzner Object Storage,
# AWS, Cloudflare R2, MinIO). freehire-ops owns the bucket + credentials in prod.
S3_ENDPOINT=
S3_BUCKET=
S3_ACCESS_KEY=
S3_SECRET_KEY=
# CV builder PDF rendering (optional). Path/name of the typst binary; resolved via
# PATH, so a bare "typst" works when it is installed locally. Empty or unresolved
# disables rendering (the /me/cvs/:id/pdf endpoint returns 501; CRUD still works). The
# prod image bakes the binary in and sets TYPST_BIN=/app/typst.
TYPST_BIN=typst
# Per-role bullet ceiling (default 20). Raise in deploy if needed (e.g. 1000).
# Restart required after change.
# CV_MAX_BULLETS=20
# Allow silent Sanitize truncation past that ceiling (default false = refuse).
# Ops kill switch; restart required.
# CV_EDIT_ALLOW_BULLET_TRUNCATION=false
# Fit-analysis sanitize caps (internal/candidate/matchanalysis). Applied to untrusted
# LLM output before it is cached or served. Unset → that package's Default* constants,
# which are the only place these numbers live; the values shown here are a copy for
# reading. Values below 1 fall back the same way. Restart required after change.
#
# MATCH_ANALYSIS_MAX_COMMENT_RUNES=240 # per-dimension Comment
# MATCH_ANALYSIS_MAX_LIST_ITEM_RUNES=200 # each Strengths / Gaps bullet
# MATCH_ANALYSIS_MAX_RECOMMEND_RUNES=1200 # free-text Recommendation
# MATCH_ANALYSIS_MAX_REQ_TEXT_RUNES=200 # Requirement.Text
# MATCH_ANALYSIS_MAX_REQ_EVIDENCE_RUNES=240 # Requirement.Evidence
# MATCH_ANALYSIS_MAX_STRENGTHS=6 # Strengths list length
# MATCH_ANALYSIS_MAX_GAPS=6 # Gaps list length
# MATCH_ANALYSIS_MAX_REQUIREMENTS=30 # RequirementMatch list length
# MATCH_ANALYSIS_MAX_SIGNALS=5 # HiddenSignals list length
# MATCH_ANALYSIS_MAX_SIGNAL_QUOTE_RUNES=200 # Signal.Quote
# MATCH_ANALYSIS_MAX_SIGNAL_INSIGHT_RUNES=200 # Signal.Insight
# Keys the visitor hash of a click on a traced CV link (opt-in, per CV). Leave it EMPTY and the
# feature cannot be switched on at all: enabling the toggle returns 409, no token is minted, and
# nothing is recorded. That is the intended state until the privacy policy describes what a traced
# link stores. Any long random string; changing it makes past visitors look like new ones.
TRACER_LINK_SALT=
# --- LLM (optional) ----------------------------------------------------------
# Any OpenAI-compatible /v1 endpoint. Unset → AI features stay disabled (catalogue
# still works). Local AWS Bedrock via compose profile `bedrock` (SSO — no long-lived
# IAM keys in this file):
#
# aws sso login --profile dev
# podman compose --profile bedrock up -d --build bedrock-proxy
#
# LLM_BASE_URL=http://127.0.0.1:4000/v1
# LLM_API_KEY=local-litellm
# LLM_MODEL=bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0
# ASSISTANT_MODEL=bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0
#
# Do NOT put AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY here for Bedrock — they are
# long-lived, land in shell history and `podman inspect`, and skip SSO MFA.
LLM_BASE_URL=
LLM_API_KEY=
LLM_MODEL=
# Optional sampling temperature (0–2), for an endpoint that rejects or mishandles the
# default. Unset does NOT omit the field: langchaingo v0.1.15 sends "temperature":0 on every
# call to a non-reasoning model id (it drops the field only for reasoning ids). Setting it
# changes EVERY client in the process that reads this env — all of cmd/server's clients,
# search intent and the ATS review included — and any worker sharing the env file. Set it
# only in a server-only env file, never in the shared one enrichment reads.
# LLM_TEMPERATURE=
# ASSISTANT_MODEL=
# Max runes in one user message to the assistant (default 8000). Raise locally if you
# paste long briefs (e.g. 15000). Restart required after change.
# ASSISTANT_MAX_PROMPT=8000
# Speech-to-text for the assistant composer's microphone (optional, and OFF until you
# name a model). It runs on the SAME gateway and key as the rest of the LLM stack — an
# OpenAI-compatible endpoint serves /chat/completions and /audio/transcriptions alike —
# so there is nothing else to configure.
#
# There is deliberately NO default: transcription is billed per minute of audio and an
# assistant turn is not metered, so silence here means no microphone rather than a
# model somebody did not choose. Empty (or LLM_BASE_URL/LLM_API_KEY unset) leaves the
# transcription endpoint answering 501 and the composer rendering no microphone.
STT_MODEL=
# Langfuse LLM tracing (all optional — tracing is disabled unless all three are
# set, exactly like MEILI_MASTER_KEY gates search). Traces every enrich /
# tg-extract model call: prompt, response, token usage, latency. Get the keys
# from your Langfuse project settings. US region base URL shown; use the EU or
# self-host URL if that is where your project lives.
LANGFUSE_BASE_URL=https://us.cloud.langfuse.com
LANGFUSE_PUBLIC_KEY=
LANGFUSE_SECRET_KEY=
# --- Typesafe AI / Jev (optional) --------------------------------------------
# Backs ONLY the browser extension's autofill agent answering a combobox's closed
# option list (internal/ai/autofillagent.JevPlanner.Choose). Unrelated to LLM_* —
# read fresh, not shared with any other feature. Empty disables it outright:
# Choose then answers through the LLM exactly as it did before this setting existed.
TYPESAFE_API_KEY=
# Email notifications for filter subscriptions, sent via AWS SES by the notify
# worker (cmd/notify). Optional — the email channel is registered only when BOTH
# are set; either empty and the worker still delivers Telegram. AWS credentials
# come from the default AWS chain (env AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY or
# an IAM role), never from here. NOTIFY_EMAIL_FROM must be an SES-verified sender
# on a domain you control (freehire-ops owns the SES identity in prod).
AWS_REGION=
NOTIFY_EMAIL_FROM=