Configuration
DM ships defaults for ordinary use, so most deployments set only the secrets and URLs below. Change a setting
explicitly when your machine or deployment needs something else. The service reads its settings from the environment
at start and refuses to start, naming the variable and the reason, when one is missing, malformed or unsafe;
.env.example at the repository root lists every service setting with its default.
The dm command
| Setting | Default | Purpose |
|---|---|---|
DM_API_URL / dm login --api-url |
https://api.dm.teamofsilicons.com |
The DM service a profile signs in to (remembered by the profile) |
ACCOUNTS_URL / dm login --accounts-url |
https://accounts.teamofsilicons.com |
The Silicon Accounts that signs DM in (remembered by the profile) |
DM_APP_ID |
dm |
DM's app id at Silicon Accounts; only for a development app |
SILICON_DM_HOME |
unset | An explicit, absolute state directory (takes precedence) |
dm config home LOCATION |
unset | Keep state under LOCATION/.silicon-dm |
SILICON_HOME |
the operating-system home | Parent of .silicon-dm when nothing above is set |
SILICON_DM_RELAY_HOME |
~/.silicon-dm/relay of the operating-system user |
Where the shared relay keeps its files |
--profile NAME / dm profiles use |
default |
Which signed-in account a command uses |
--wait-seconds N |
30 | How long a command waits for the relay; on timeout the request stays queued |
dm daemon start --port PORT |
the last port used, else 19780 | The relay's preferred loopback port |
DM records no telemetry.
Plain http URLs are accepted only for this machine (localhost, *.localhost, loopback addresses). Keep the state
directory on a durable local disk: it holds the sign-ins and the outgoing queue. One relay serves every state directory
of the operating-system user.
dm accounts --json shows the values in effect without signing in. Setting SILICON_DM_TEST or DM_TEST_APP_SECRET
(test environments, retired) makes every networked command refuse with testing_environments_retired, so a home that
still selects a test environment never reaches the live service.
Installing and updating
Silicon Apps installs and updates the command: silicon-apps install dm (development releases:
silicon-apps install 'dm>dev'). DM never replaces its own executable. A running relay keeps serving after an
update; the next dm command from the new version replaces an older relay, and queued requests survive. Rust programs
manage silicon-dm-client as an ordinary dependency.
The service
Sign-in
Sign-in is Silicon Accounts only:
| Variable | Required | Purpose |
|---|---|---|
ACCOUNTS_URL |
yes | Silicon Accounts origin; exactly the iss of its tokens |
ACCOUNTS_API_URL |
no | Where DM calls Silicon Accounts server to server (default ACCOUNTS_URL) |
DM_APP_ID |
no | DM's app id in Silicon Accounts (default dm) |
DM_APP_SECRET |
yes | DM's app secret |
DM_ACCOUNTS_WEBHOOK_SECRET |
yes | The app webhook signing secret (whsec_…); webhooks arrive at /webhook/ |
DM_ACCOUNTS_REQUEST_TIMEOUT_SECONDS |
no | Timeout of each call to Silicon Accounts (default 5) |
DM_DATA_KEY |
yes | 32 random bytes, base64 (openssl rand -base64 32): keys sync cursors and the encryption of stored proofs. Keep it stable and backed up |
DM_PROOF_ISSUERS |
no | Apps whose User verification proofs DM accepts, per scope (dm.messages.write=interface,…); empty accepts none |
DM's sign-in setup at Silicon Accounts needs device_flow and public_client turned on for the command, and
requires no account details (email and timezone are optional).
The server and the database
| Variable | Default | Purpose |
|---|---|---|
DM_ENVIRONMENT |
development |
development, test or production. Production requires HTTPS upstreams and a verified database connection |
DM_BIND_ADDR |
127.0.0.1:8080 |
Where dm-api listens |
DM_PUBLIC_BASE_URL |
required | The public API base, such as https://api.dm.teamofsilicons.com/api/v1 |
DM_REQUEST_TIMEOUT_SECONDS |
15 |
Ordinary requests (the sync long-poll, speech and transcription have their own, longer limits) |
DM_SHUTDOWN_TIMEOUT_SECONDS |
30 |
Graceful shutdown deadline |
DM_MAX_HTTP_BODY_BYTES |
134217728 (128 MiB) |
Largest request body (1 byte to 3 GiB) |
DM_DATABASE_URL |
required | PostgreSQL; production needs exactly one sslmode=verify-full |
DM_DATABASE_MAX_CONNECTIONS, DM_DATABASE_MIN_CONNECTIONS |
16, 1 |
The connection pool for queries. Each process also opens up to three connections of its own for LISTEN wake-ups, outside it (dm-api three, dm-worker two; none with a pool of one) |
DM_DATABASE_ACQUIRE_TIMEOUT_SECONDS |
5 |
Waiting for a pooled connection |
DM_DATABASE_STATEMENT_TIMEOUT_SECONDS |
10 |
One statement's limit in dm-api and dm-worker; dm-migrate runs without it (a backfill must not be cut short) |
DM_LOG_FILTER |
info,silicon_dm=debug |
Log levels. Whatever the level, DM never logs scripts, audio, transcripts or credentials |
Plain http is accepted only for this machine.
Speech
Speech (voices, spoken messages, listening, transcription) goes through Deepgram from the service only; the key never reaches a client:
| Variable | Default | Purpose |
|---|---|---|
DM_DEEPGRAM_API_KEY |
unset | Deepgram key. Unset: speech and transcription answer 503 speech_not_configured, and GET /api/v1/accounts says speech.enabled: false |
DM_DEEPGRAM_URL |
https://api.deepgram.com |
Deepgram origin for transcription (/v1/listen); no path |
DM_DEEPGRAM_AGENT_URL |
wss://agent.deepgram.com/v1/agent/converse |
Deepgram Voice Agent endpoint that renders speech |
DM_SPEECH_MODEL |
eleven_v4 |
The ElevenLabs model scripts are spoken with |
DM_STT_MODEL |
nova-3 |
The Deepgram transcription model |
DM_VOICES |
the five default voices | JSON array of {"key","name","voice_id","description"} (1 to 50, unique keys) |
DM_SPEECH_CACHE_MAX_BYTES |
2147483648 (2 GiB) |
Rendered audio kept; the least recently used beyond it is evicted |
DM_SPEECH_CACHE_TTL_DAYS |
90 |
Rendered audio unused this long is evicted |
DM_SPEECH_RENDERS_PER_MINUTE |
30 |
Renders one account may cause per minute (cache hits are free) |
DM_SPEECH_DAILY_CHARACTERS |
200000 |
Characters one account may have rendered per UTC day |
DM_TRANSCRIBE_MAX_BYTES |
104857600 (100 MiB) |
Largest audio upload a transcription accepts (at most 2 GiB) |
DM_TRANSCRIBE_PER_MINUTE |
20 |
Transcriptions one account may request per minute |
Plain http:// and ws:// Deepgram URLs are accepted only for this machine (tests and local stand-ins). The audio
cache lives in PostgreSQL (speech_audio): it is derived data, never an upload, and any row can be evicted and
rendered again. Voices and speech explains the choices.
The default voices (DM_VOICES unset):
| Key | Name | ElevenLabs voice id |
|---|---|---|
mark |
Mark | UgBBYS2sOqTuMpoF3BR0 |
brittney |
Brittney | kPzsL2i3teMYv0FxEYQ6 |
jamal |
Young Jamal | 6OzrBCQf8cjERkYgzSg8 |
jessa |
Jessa | yj30vwTGJxSHezdAGsv9 |
hope |
Hope | tnSpp4vdxKPjI9w0GnoV |
Changing the catalog changes the default voice of accounts that never chose one (the default is an index into the list); a stored choice whose key is no longer listed falls back to the default. Spoken messages keep the voice they were sent in either way.
Delivery through Ting, presence and GIFs
| Variable | Default | Purpose |
|---|---|---|
DM_TING_URL |
unset | Ting delivery; unset means clients sync with GET /api/v1/sync |
DM_TING_APP_ID |
ting |
Ting's app id in Silicon Accounts: the receiving app of the proofs DM sends Ting |
DM_TING_REQUEST_TIMEOUT_SECONDS |
15 |
Calls to Ting |
DM_DELIVERY_BATCH_SIZE |
100 |
Ting handoffs claimed at once |
DM_DELIVERY_POLL_INTERVAL_MILLISECONDS |
250 |
How often the handoff worker looks for work when idle |
DM_DELIVERY_LEASE_SECONDS |
30 |
How long a claimed handoff is held before another worker may take it |
DM_DELIVERY_MAX_ATTEMPTS |
20 |
Attempts before a handoff is set aside |
DM_DELIVERY_MAX_RETRY_SECONDS |
300 |
Longest pause between attempts |
DM_HEARTBEAT_INTERVAL_SECONDS, DM_HEARTBEAT_TIMEOUT_SECONDS |
30, 120 |
A device's presence lease: clients renew every 30 seconds, and a lease not renewed for 120 seconds ends (both fixed) |
DM_PRESENCE_ACTIVITY_TTL_SECONDS |
10 |
How long an activity (typing…) shows without being renewed |
DM_WS_OUTBOUND_CAPACITY |
256 |
Kept for older configurations; DM serves no WebSockets |
DM_GIPHY_API_KEY |
required | Giphy key for trending and search |
DM_GIPHY_API_BASE_URL |
https://api.giphy.com/v1/gifs |
Giphy's API |
DM_PROVIDER_REQUEST_TIMEOUT_SECONDS |
10 |
Calls to Giphy |
DM_TRENDING_GIF_CACHE_SECONDS |
300 |
How long trending GIFs are cached |
Attachments are external links; no upload credentials are required by the service.
Bug report notifications
Set DM_POSTMARK_SERVER_TOKEN on the API service to send report notifications. The default DM_POSTMARK_EMAIL_URL is
https://api.postmarkapp.com/email. Verify dm@teamofsilicons.com as a Postmark sender. Reports notify
saketdev12@gmail.com, shubhastro2@gmail.com and bugs@teamofsilicons.com.
Each account may submit DM_REPORTS_PER_HOUR new reports an hour (default 10, at most 100000); the next one is 429
rate_limited, whose Retry-After says when one fits again. Retries with the same idempotency key do not create
another report.
The API returns 202 once the report is stored. Its worker retries Postmark failures with backoff, up to an hour
between attempts. A process crash after Postmark accepts but before the transaction commits can deliver a duplicate
notification; the report id identifies it. Outside production, without a Postmark token, reports are stored and marked
simulated. No mail token is distributed in the client or the command.
Settings DM no longer reads
The settings of the sign-in and installer services DM used before Silicon Accounts and Silicon Apps, of the retired test environments and of telemetry are ignored. The service names any that are still set once at start, so an operator can remove them.
The website
The website's server (Next.js) reads these at request time; none of them reaches the browser:
| Variable | Purpose |
|---|---|
APP_ID |
dm |
APP_SECRET |
DM's app secret at Silicon Accounts (the server-side sign-in) |
ACCOUNTS_URL, ACCOUNTS_API_URL |
Silicon Accounts, as for the service |
APP_API_URL |
The DM service the website calls through its same-origin proxy |
SESSION_SECRET |
At least 32 random bytes; seals the session cookie (changing it signs every browser out) |
PUBLIC_URL |
The website's own origin; {PUBLIC_URL}/auth/callback is registered at Silicon Accounts |
EXTRA_IMG_ORIGINS, EXTRA_ORIGINS |
More trusted profile-photo origins; another explicitly trusted website origin |
BRIEFCASE_API_URL |
Silicon Briefcase's API origin, where the website saves a Carbon's files and voice notes (in apps/dm/ of the Carbon's own Briefcase). Unset: uploads are off and the website says so; links and GIFs still work |
UPLOADS_MAX_BYTES |
Optional: a limit of the website's own on one file, in bytes, for an operator who wants one. Unset (the default), the website takes files of any size up to Silicon Briefcase's 5 TiB |
KEEP_ALIVE_TIMEOUT |
Optional, in milliseconds (Next's own setting): how long the website keeps an idle connection. Unset, it keeps one 185 seconds, longer than a load balancer keeps its own (production's: 180); behind a balancer whose idle timeout is longer, set it above that, or the balancer now and then reuses a connection the website is closing and answers 502 |
The DM service never handles files: uploads are the website's job, done as DM acting for the Carbon at Silicon
Briefcase with User verification proofs. The website's server passes each file's bytes through to Briefcase as they
arrive and keeps none of them, so it needs no disk for uploads and its memory does not grow with a file's size; what
sits in front of it must not limit or buffer the upload's body (the website's ingress).
Production needs Briefcase to accept DM's proofs for its upload scopes
(the Briefcase side). web/README.md in the repository
covers running and deploying the website.
When uploads are off
When uploads cannot work, the website tells Carbons only that, in their words: GET /api/uploads/config answers
{"enabled": false, "reason": …}, the + menu says the reason under its switched-off Upload a file and offers links and
GIFs instead, a file pasted or dropped is answered with the same reason (a dropped link still attaches), /upload and
⌘⇧U say it too, and the microphone offers dictation only.
Which setting is wrong is the operator's business, so the website's server writes it to its log, once per process, as
a line that starts dm: Uploads are off:. What each one means:
| Carbons read | The server logs | Fix |
|---|---|---|
| "Uploads are not set up on this DM website yet." | BRIEFCASE_API_URL is not set |
Set BRIEFCASE_API_URL to Briefcase's API origin, such as https://api.briefcase.teamofsilicons.com (after Briefcase accepts DM's proofs) |
| "Uploads are not working on this DM website: its setup needs fixing." | BRIEFCASE_API_URL is not a URL, … must start with https://, … must be an origin (and a path) without credentials, a query or a #fragment |
Give the origin alone; plain http works only for a stand-in on this machine |
| the same | UPLOADS_MAX_BYTES is not a whole number of bytes |
A whole number of bytes, such as 1073741824 (1 GiB), or unset for no limit of the website's own |
Once uploads are on, a refusal by Silicon Accounts or Briefcase reaches the Carbon as the upload's own error
(Errors). 503 briefcase_not_allowed means Briefcase does not list dm in its
BRIEFCASE_PROOF_ISSUERS for a scope an upload needs: before anything is saved, or after the file is saved when only
briefcase.link_access.update is missing (the Carbon then reads that the file was saved but its link could not be
opened, and where the copy is). 503 uploads_misconfigured means the website's APP_ID or APP_SECRET is wrong. For
these, and for every other failure that points at Briefcase or Silicon Accounts (a 5xx), the website's server logs one
line with Briefcase's own code, never a file's name: upload failed: briefcase_not_allowed (proof_scope_missing). A
cancel Briefcase refuses is logged as upload: Silicon Briefcase did not cancel an unfinished upload (…): that upload
stays pending there until it expires, within a day.
Ting receiver origin on the website
TING_BROWSER_ORIGIN is optional. Set it to the exact Ting origin that issues
its browser cookie, for example https://ting.teamofsilicons.com. It accepts
HTTPS, or HTTP on loopback for local testing, with no path, credentials, query,
fragment or wildcard. Leaving it unset keeps DM's HTTP recovery reader.
Also allow the website's exact PUBLIC_URL in Ting's browser-origin settings.
The site adds only that Ting origin and its corresponding wss: origin to CSP;
DM API calls and mutations stay on the website's own origin. Both sites must
support Ting's SameSite cookie policy; a CORS allowlist alone cannot enable a
cross-site cookie. The Carbon signs into Ting as the same account separately.