-
Notifications
You must be signed in to change notification settings - Fork 0
Configuration and Integrations
Two questions this page answers: where does a given setting live, and is this integration actually talking to anything? The second one matters more than it sounds. Forge ships a complete mock implementation of every external service it can talk to, and a fresh evaluation install is configured to use them — so it is entirely possible to click through invoicing, shipping, address validation and the AI assistant on a laptop and conclude that all of it works, when every call returned fabricated data.
Canonical reference: docs/functional-reference/integrations.md, with one caveat — the parts of it written in terms of appsettings.json and compose environment variables describe the fallback layer, not the primary one. It predates the descriptor-driven settings system described below.
.env on the host owns everything that must exist before the process starts: image tags, bind addresses and ports, the database connection, JWT_KEY, FRONTEND_BASE_URL and CORS_ORIGINS, storage endpoint and credentials, the backup schedule, the schema-reconcile switch, and SSO provider registration. It is never committed, it is not in a backup snapshot, and you edit it with a text editor and restart the stack. See Installation for how it gets written and Backup and Restore for what you must hold separately.
The admin settings screens own everything an integration needs to talk to a third party. Each setting is declared in code as a descriptor — key, group, display name, data type, default, validation pattern and an is-secret flag — and the runtime value is a row in the system_settings table. Secrets are sealed with ASP.NET Data Protection before storage and unsealed on read; the UI shows a mask rather than the value, and a mask sent back on save is detected and skipped, so re-saving a form can never corrupt a stored secret. Writes are validated against the descriptor, rejected if the key does not belong to the integration being edited, and committed in one transaction.
The practical consequence is the one operators get wrong: integration credentials are rows in your database, not lines in a file, so they survive image upgrades and they are not fixed by editing .env. Where an integration is still bound to environment-backed options — the accounting providers and per-user cloud storage — a boot-time hydrator overlays the admin-saved values on top of the environment binding after startup, so a value stored in the database wins over the same value in .env. Everything else (mail, object storage, and the carriers described below) reads the settings table at call time.
Each integration card in the admin UI carries the provider's own signup link and a step-by-step walkthrough for obtaining the credential. The wiki does not reproduce provider instructions; it tells you the screen exists.
Every external integration resolves to one of three implementations when the API starts:
| Mode | Behaviour |
|---|---|
| Mock | Canned data. Nothing leaves the box. |
| Real | The actual provider, using the credentials you stored. |
| Disabled | The feature is off and its endpoints refuse the call. |
Mode is per integration — one install can run real QuickBooks against mock mail — and it is stored as that integration's own {provider}.mode setting. Two things decide the effective mode. An explicit Mock, Real or Disabled choice always wins. Otherwise the install's posture, the MOCK_INTEGRATIONS flag, decides: in a mock posture an integration stays mocked unless you explicitly set it to Real, so configuring a credential on a dev box never silently starts hitting a live service; in a real posture an integration adopts its real implementation once its primary credential is set, and falls back to the mock rather than crashing when it is not. An explicit Auto mode opts into credential-based resolution regardless of posture.
The trap worth checking before you trust anything: the shipped .env.example starts at MOCK_INTEGRATIONS=true. setup.sh flips it to false for the network-facing install paths — LAN, standalone and cohost — but a purely local evaluation install can be left in the mock posture. If everything appears to work perfectly and you never entered a credential, that is why.
Modes are read at startup, straight from the settings table before the dependency-injection graph is built, so switching an integration between Mock and Real takes effect on restart, not on save. The UI says so; it is not a bug. Credential edits are a different matter — for the providers that bind options, a save is pushed into the running process immediately, and the services that read the settings table per call pick up a change on the next call.
One integration family has an extra rule. At most one accounting provider may be in Real mode at a time; choosing Real on one flips the others to Disabled. That is the same mutual exclusion described in Accounting Modes, expressed at the credential layer rather than the capability layer.
Capabilities and integrations are two halves of one decision, and Forge joins them for you. Each integration descriptor may name the capability whose being on makes that integration needed — the AI assistant needs a model host, external accounting needs a provider connection, email sync needs an OAuth client. Infrastructure integrations with no gating capability, such as mail and object storage, are treated as recommended rather than required.
The readiness check crosses the descriptor catalog against your stored settings, the live capability snapshot and the environment posture, and classifies each integration:
| State | Meaning |
|---|---|
| Not needed | Its capability is off. No nagging. |
| Configured | Its primary credential is set. |
| Mock | Unconfigured, but you are not in a production posture, so the mock covers it. |
| Gap | Production posture, capability on, credential missing. Actionable. |
| Optional | Production posture, unconfigured, but no capability depends on it. |
It runs twice. At boot it writes to the API log under an [INTEGRATION-READINESS] label: a warning naming each gapped integration with its capability code, and a separate, louder warning if the install is running in a Production environment while globally forced to mock — which means every external integration is returning canned data. In the app it drives the readiness state shown beside each integration card.
So the post-install checklist is short. Pick your capabilities through the discovery wizard (Capability Gating), then read the readiness report and close every gap. A capability being on does not mean the feature is wired, and this report is how you tell the difference. Where a call is refused because the capability itself is off, see API Access for the shape of that refusal.
Categorical, not versioned — this is the honest shape of each lane rather than a feature matrix.
Real connectors, ready to configure. Accounting: QuickBooks Online (the primary), Xero, FreshBooks, Sage, NetSuite, Wave and Zoho, all behind one interface — see Accounting Modes. Shipping: UPS, FedEx, USPS and DHL for rates, labels and tracking, aggregated so a shop can rate-shop across whichever carriers it has configured. Address validation against USPS. Outbound mail over any SMTP server. Object storage on MinIO, any S3-compatible endpoint, or the local filesystem. AI on a self-hosted model host, so prompts stay inside the stack. E-signature on DocuSeal, self-hosted or cloud. Inbound and outbound mail logged against leads and contacts over IMAP, with OAuth for Google Workspace and Microsoft 365 and plain credentials for anything else. Call logging via Twilio webhooks. Per-user cloud storage on Google Drive, OneDrive or Dropbox, per-user calendar destinations (Google Calendar, Outlook, an ICS feed) and per-user chat destinations (Slack, Teams, Discord, Google Chat).
Real but partial. EDI: the X12 translator is real and runs in every posture, because translation is logic and database work rather than an external call. The transport resolves per trading partner, and SFTP is the channel that is actually implemented — partners configured for AS2, VAN, email or API fall through to a manual no-op channel, meaning you exchange those documents by hand. E-commerce: Shopify and WooCommerce are real HTTP connectors; the marketplaces are deliberately not registered, and the factory raises a clear error rather than pretending to poll. Selling through channels at all is its own shape — see Business Shapes.
Placeholder today. Bank payment origination for ACH and wire, CPQ, machine data and IoT collection, BI export, plant context, and several inventory and maintenance analytics services register a mock in every posture, because no real adapter exists yet. They have working interfaces and working screens, and they return fabricated data. Treat them as design surface. Machine data is the one most likely to be assumed working — a machine connection record is configuration with no collector behind it, which matters if you are evaluating Forge as an MES.
Two repo docs to read with care against this list: docs/functional-reference/shipments.md still says direct carrier APIs are "planned but not yet implemented", which is no longer true, and docs/functional-reference/edi.md lists AS2, VAN, email and API among its transport methods, which describes the options you can select on a trading partner rather than the channels that carry documents. Where a doc and the code disagree, the code wins — see Documentation Map.
The durable rule, rather than a list that rots: the authoritative answer is the service registration in the API's startup, and the readiness report on your own install tells you what your box is running.
Forge supports three sign-in federations, each enabled independently: Google, Microsoft, and a generic OIDC provider — Keycloak, Authentik, Okta, anything with a discovery document, where you supply the authority, client id, client secret and a display name for the button.
Only Google is wired through the shipped .env and compose file. Microsoft and generic OIDC are fully implemented in the API but have no environment variables in the stock deployment, so enabling them means adding the corresponding configuration keys through a docker-compose.override.yml.
The rule that surprises people: SSO does not provision accounts. On a first federated sign-in Forge looks for an existing active local user with the same email address and links the external identity to it. If there is no such user, the sign-in is refused with "No account found. Contact your administrator to create an account first." Create the user in Forge first and the link happens automatically on their first federated login. Roles come from the local account, expanded through role templates — never from the identity provider.
Two optional restrictions are worth knowing. An email-domain allow-list limits which domains may sign in. It is enforced identically for the browser flow and the token-exchange endpoint, comparison is case-insensitive, wildcards are not supported, and subdomains are not implied — list each permitted domain explicitly. There is a real footgun here: an allow-list wired to an environment variable that can be empty binds as a single empty entry, which rejects every sign-in. That is why the shipped compose file deliberately leaves it unwired; set it as an explicit list in an override file, or leave it out entirely. For Microsoft there is also a tenant allow-list, which lets one install trust several named Microsoft tenants without dropping to single-tenant mode.
Beyond the browser flow there is a token-exchange endpoint that trades a provider id_token for a Forge session, for a companion application that has already signed the user in. One sharing Forge's own OAuth client needs no extra configuration; one with its own client needs that client id added as an accepted audience. The contract is in docs/api-key-integrations.md; see also API Access.
Hardening the rest of the sign-in surface — environment, CORS, rate limiting, what answers without a Forge login — is Hardening a Production Install.
Carrier credentials are deliberately not on the integrations page. They live on a dedicated carriers screen, so there is one home for carrier setup and no duplicated credential entry. An operator who looks for UPS among the integration cards and does not find it should look there.
A carrier is a record you create, and it carries an integration kind. A manual carrier covers custom or shadow shippers and any known carrier you use without a live API — you record tracking numbers by hand. An API carrier is bound to one of the built-in adapters and can rate-shop, buy labels and pull tracking. The kind drives where labels come from and whether delivery updates automate; it is independent of the per-carrier scan-to-ship requirement, which is its own flag.
Credentials are entered per carrier and stored encrypted, and the shipping adapters read them live from the carrier record on every call — so there is no cache to invalidate and no restart after a credential change. Each carrier also carries an environment, sandbox or production, which defaults to sandbox. Check it before your first real label.
The screen has a Test Connection action that runs a sample rate quote through exactly the path shipping will use, and it is written to explain rather than throw. It will tell you that the carrier is manual and has nothing to test; that the API is running in mock mode, so no real adapter is loaded; that the credentials are simply not filled in; or that the carrier connected but returned no rates — which usually means the account number or API permissions are wrong rather than the credentials. Carrier configuration is restricted to the admin, manager and office-manager roles, and the whole screen sits behind the shipping capability.
Related: Installation for first boot · Capability Gating for which features exist at all · Customizing an Install for wording, languages and branding · Upgrades and Rollback for what happens to settings across an image change · Operations and Troubleshooting for reading the logs these subsystems write.
Forge · Apache 2.0 · built by Armory Works — this wiki maps the docs; the authoritative detail lives in docs/.
Evaluating
Running it
- Installation
- First Week
- Configuration and Integrations
- Hardening a Production Install
- Backup and Restore
- Upgrades and Rollback
- Accounting Modes
Using it
- App Surfaces
- Shop Floor Kiosk
- Mobile and Offline
- Access and Roles
- Customizing an Install
- Feature Reference
- UI Flows
Building on it
- API Access
- Architecture
- Capability Gating
- Workflow, Gates and Approvals
- Data Ownership and Export
- Glossary
Contributing
Repo wikis