Deployment¶
Running ARC-1 for more than one person — shared team server, BTP Cloud Foundry, or a hosted instance that multiple MCP clients hit.
For single-developer setups on your own laptop, use local-development.md instead.
Decision tree¶
Who authenticates to SAP?
| Answer | SAP auth to pick | Per-user SAP audit? |
|---|---|---|
| Everyone shares a service account | Basic Auth | ❌ Shared identity |
| Destination Service resolves it (on-prem via Cloud Connector) | BTP Destination | Depends on destination type |
Destination uses PrincipalPropagation + Cloud Connector |
Principal Propagation | ✅ Per-user |
| BTP CF app connects to BTP ABAP Environment | Destination OAuth2UserTokenExchange |
✅ Per-user |
| Local developer connects to BTP ABAP Environment | BTP service-key OAuth + browser login | ✅ One local user; not headless |
Who authenticates to ARC-1 (the MCP endpoint)?
| Answer | MCP auth to pick |
|---|---|
| Everyone shares one token | API Key |
| IdP-issued JWT (Entra ID, Okta, Keycloak, Cognito, …) | OIDC |
| Running on BTP CF with XSUAA | XSUAA OAuth |
| Mix of the above | All three — they chain, see enterprise-auth.md |
Where does ARC-1 run?
| Answer | Path |
|---|---|
| Docker on any VM / container host | Docker deployment |
| BTP Cloud Foundry, on-prem SAP via Cloud Connector | BTP CF with PP |
| BTP Cloud Foundry, BTP ABAP backend | BTP CF + BTP ABAP |
Docker on any VM¶
Run the published image on any host with Docker. Works for on-prem SAP reachable from the host, or BTP ABAP if the host can route to *.abap.*.hana.ondemand.com.
Shared service account + API Key¶
docker run -d --name arc1 -p 8080:8080 \
-e SAP_URL=https://your-sap-host:44300 \
-e SAP_USER=SVC_ARC1 -e SAP_PASSWORD=... \
-e SAP_CLIENT=100 \
-e ARC1_API_KEYS="$(openssl rand -hex 32):admin" \
-e SAP_ALLOW_WRITES=true SAP_ALLOW_TRANSPORT_WRITES=true \
-e SAP_ALLOWED_PACKAGES='Z*,$TMP' \
ghcr.io/arc-mcp/arc-1:latest
MCP clients pass Authorization: Bearer <api-key> when connecting to http://host:8080/mcp.
Shared service account + per-user OIDC¶
Adds per-user identity on top of ARC-1 (Layer A) while still sharing one SAP user (Layer B):
docker run -d --name arc1 -p 8080:8080 \
-e SAP_URL=https://your-sap-host:44300 \
-e SAP_USER=SVC_ARC1 -e SAP_PASSWORD=... \
-e SAP_OIDC_ISSUER=https://login.microsoftonline.com/{tenant}/v2.0 \
-e SAP_OIDC_AUDIENCE={client-id-guid} \
ghcr.io/arc-mcp/arc-1:latest
This example only turns on OIDC validation for the MCP endpoint. It does not widen the server's safety ceiling: ARC-1 still defaults to read-only, no SQL, no named table preview, no transport writes, and writes restricted to $TMP unless you set explicit SAP_ALLOW_* safety flags.
If this shared server should allow development work, add these flags to the same docker run command:
Per-user JWT scopes and API-key profiles sit beneath that server ceiling — they can only tighten, never widen. A user with the write scope still cannot mutate objects when SAP_ALLOW_WRITES=false. Full model: authorization.md. Every flag: configuration-reference.md.
ARC-1 audit logs show the real MCP user; SAP audit logs show the shared service account. Trade-off — good compromise when you can't use PP.
For this shared-user mode, ARC-1 runs a startup auth preflight (/sap/bc/adt/core/discovery) and blocks SAP tool calls on 401/403 with a clear remediation message. This avoids hammering SAP with repeated failed logins when the technical password/client is wrong.
Full references: - docker.md — image tags, build, ports, troubleshooting - api-key-setup.md — one or more API-key entries with profiles - oauth-jwt-setup.md — OIDC with Entra ID / Okta / Keycloak - security-guide.md — production hardening checklist
BTP Cloud Foundry with Principal Propagation¶
The only deployment path that gives true per-user SAP identity with on-prem SAP. Each MCP user's JWT is exchanged for a SAML assertion via Cloud Connector → SAP sees the real user → S_DEVELOP / audit logs / change history all attribute to the human.
You'll need¶
- BTP subaccount with a Cloud Foundry space
- Cloud Connector installed in your network, mapping your on-prem SAP
- BTP Destination Service with a destination of type
HTTP+ authenticationPrincipalPropagation - BTP XSUAA for MCP client auth
- Optional: BTP Audit Log Service
Shape¶
MCP client (user JWT) → XSUAA validates → ARC-1 on CF
│
▼
Destination Service (PP)
│
▼
Cloud Connector
│
▼
On-prem SAP (real user)
Config¶
cf set-env arc1 SAP_BTP_DESTINATION MY_SAP_DESTINATION
cf set-env arc1 SAP_BTP_PP_DESTINATION MY_SAP_PP_DESTINATION
cf set-env arc1 SAP_PP_ENABLED true
cf set-env arc1 SAP_PP_STRICT true # fail closed — don't run as the shared service account if PP fails
cf set-env arc1 SAP_XSUAA_AUTH true
cf set-env arc1 SAP_ALLOW_WRITES true && cf set-env arc1 SAP_ALLOW_TRANSPORT_WRITES true
cf set-env arc1 SAP_ALLOWED_PACKAGES 'Z*'
Set SAP_PP_STRICT=true for per-user deployments
Without it, a principal-propagation failure silently falls back to the shared service account — the request runs with the wrong (broader) identity and SAP audits the technical user, not the human. See Principal Propagation Setup.
Startup summary:
Full references:
- btp-cloud-foundry-deployment.md — MTA + Docker push, manifest.yml, service bindings, step-by-step
- principal-propagation-setup.md — Cloud Connector config, destination types, certificate chain
- btp-destination-setup.md — destination configuration details
- xsuaa-setup.md — xs-security.json, scopes, role collections
BTP Cloud Foundry + BTP ABAP Environment¶
ARC-1 deployed on CF, backend is a BTP ABAP (Steampunk) system. No Cloud Connector needed — both sides are on BTP.
SAP auth is OAuth2 via a BTP Destination with OAuth2UserTokenExchange. The ABAP service key is used to create the destination's OAuth client settings, but it is not mounted into ARC-1 and ARC-1 does not run the local browser flow. Per request, XSUAA authenticates the MCP user, the Destination service exchanges that user token for an ABAP-context bearer token, and SAP sees the real ABAP user.
cf create-service xsuaa application arc1-xsuaa -c xs-security.json
cf create-service destination lite arc1-destination
# Create destination ABAP_PP with Authentication=OAuth2UserTokenExchange
cf set-env arc1 SAP_SYSTEM_TYPE btp
cf set-env arc1 SAP_XSUAA_AUTH true
cf set-env arc1 SAP_PP_ENABLED true
cf set-env arc1 SAP_PP_STRICT true
cf set-env arc1 SAP_BTP_DESTINATION ABAP_PP
Full reference: btp-abap-environment.md.
Hardening checklist¶
For any deployment visible to a network, before you open the gate:
The safety ceiling is your prompt-injection backstop
ARC-1 feeds SAP-resident content (source, comments, error text) to the LLM, which then issues the next tool calls under the user's identity — a poisoned ABAP comment is an attack. SAP_ALLOW_WRITES=false and a tight SAP_ALLOWED_PACKAGES are the controls that hold regardless of what the model decides; enabling writes or * is a deliberate risk decision, not a convenience.
- [ ] TLS terminated by a reverse proxy or platform (never HTTP on a public port)
- [ ]
ARC1_API_KEYSor OIDC / XSUAA configured — never run HTTP mode without Layer A auth - [ ]
SAP_ALLOW_WRITES=falseunless you've deliberately enabled writes - [ ]
SAP_ALLOWED_PACKAGESset to a specific allowlist, not* - [ ]
SAP_ALLOW_DATA_PREVIEW=falseandSAP_ALLOW_FREE_SQL=falseunless you need them - [ ]
SAP_ALLOW_TRANSPORT_WRITES=falseunless you need CTS management - [ ]
SAP_ALLOW_GIT_WRITES=falseunless you need gCTS/abapGit writes (reads are always allowed when the backends are available) - [ ]
SAP_PP_STRICT=trueif principal propagation is on — otherwise a PP failure silently runs as the shared service account (wrong identity + wrong SAP audit) - [ ]
ARC1_RATE_LIMITset (e.g.60) for multi-user instances — the per-user MCP quota is off by default, so one runaway agent loop can saturate the shared SAP request semaphore - [ ]
SAP_INSECURE=false(the default) — the bundledmanifest.yml/mta.yamlship"true"for the Cloud Connector path; flip it on CA-signed landscapes - [ ] If using cookies:
SAP_PP_ENABLED=trueand cookies both set? → refuses unlessSAP_PP_ALLOW_SHARED_COOKIES=trueescape hatch is explicit - [ ] Audit log sink configured (file or BTP Audit Log Service) — note the file/BTP sinks contain un-redacted SAP source/error snippets, so restrict their permissions and rotation
- [ ]
ARC1_CACHE=memory/noneor an encrypted volume on IP-sensitive landscapes — the SQLite cache stores SAP source in cleartext at.arc1-cache.db - [ ] Image pinned to an exact version (for example
:0.9.18), not:latest - [ ] Update procedure rehearsed → updating.md
Full production hardening guide: security-guide.md.
Coexistence rules¶
ARC-1 fails fast at startup on unsafe combinations. See the Coexistence Matrix for the full table. The ones that most often bite:
| Combo | Result |
|---|---|
SAP_PP_ENABLED=true + SAP_COOKIE_FILE / SAP_COOKIE_STRING |
❌ startup error (unless SAP_PP_ALLOW_SHARED_COOKIES=true) |
SAP_BTP_SERVICE_KEY + cookies |
❌ startup error |
SAP_BTP_SERVICE_KEY + SAP_PP_ENABLED=true |
❌ startup error |
SAP_DISABLE_SAML=true + BTP |
⚠️ warning, continues (will break BTP ABAP / S/4 Public Cloud) |
Next¶
- All flags → configuration-reference.md
- Auth internals → enterprise-auth.md
- Update an existing deployment → updating.md
- Best practices for multi-system landscapes → deployment-best-practices.md