bf.twins.la
A digital twin of the Microsoft Bot Framework.
What is this?
A high-fidelity digital twin of two halves of the Microsoft Bot Framework that work independently:
- Channel half — emulates a channel (default
msteams). Signs channel-to-bot JWTs, publishes JWKS, issues OAuth tokens, accepts bot-to-channel POSTs. - Bot half — emulates a Bot Framework bot at
/api/messagesand validates inbound channel JWTs against a configured OpenID metadata URL.
login.botframework.com as the trusted JWKS
source. To use this twin's channel from a real bot, configure the
SDK's ToBotFromChannelOpenIdMetadataUrl
(to_bot_from_channel_open_id_metadata_url in
botbuilder-py) to point at this twin's
/v1/.well-known/openidconfiguration. The twin cannot
patch the SDK for you — the override is the consumer side
of the contract.
Supported scenarios
channel-msteams— OpenID metadata + JWKS publication, OAuth token issue, bot-to-channel activity POSTs, operator-driven inbound simulationbot-receiver—/api/messageswith channel-JWT validation, configurable trusted OpenID URL, inbox + operator-driven reply
How to use it
Cloud: Point your Bot Framework SDK's
ToBotFromChannelOpenIdMetadataUrl at
https://bf.twins.la/v1/.well-known/openidconfiguration,
and use the app_id/app_password
returned by POST /_twin/accounts.
Local: Install with
pip install twins-microsoft-bot-framework-local and
run a local instance on any port. Same API, same behavior, your
machine.
For agents
Copy this into your agent's system prompt, tool configuration, or
CLAUDE.md. Also available as plain text at
/_twin/agent-instructions.
# Microsoft Bot Framework Twin — bf.twins.la
A high-fidelity digital twin of two halves of the Microsoft Bot Framework
that work independently:
* Channel half (default channelId="msteams") — issues signed JWTs to
bots, publishes JWKS, accepts bot-to-channel activity POSTs.
* Bot half — emulates a Bot Framework bot at /api/messages and
validates inbound channel JWTs.
## Manual-trust requirement
Real Bot Framework SDKs hardcode `login.botframework.com` as the trusted
JWKS source. To use this twin's channel half from a real bot, the SDK
must be configured to trust the twin's JWKS URL:
* .NET: `ConfigurationBotFrameworkAuthentication` with a
`ToBotFromChannelOpenIdMetadataUrl` setting pointing at
`<base>/v1/.well-known/openidconfiguration`.
* Python (botbuilder-py): `AuthenticationConfiguration` with
`to_bot_from_channel_open_id_metadata_url` overridden.
* JS: `ConfigurationBotFrameworkAuthentication` with
`ToBotFromChannelOpenIdMetadataUrl` overridden.
The twin documents this URL prominently — see /_twin/settings.
## Authentication
Twin Plane: HTTP Basic (tenant_id:tenant_secret)
Bootstrap a tenant first:
POST /_twin/tenants -> {tenant_id, tenant_secret}
Twin Plane Admin: Bearer token (set by deployment owner)
Authorization: Bearer <admin_token>
Or: X-Twin-Admin-Token: <admin_token>
Provider:
Channel side: bot acquires bearer at POST /v1/.well-known/oauth2/v2.0/token
using client_id (= app_id) + client_secret (= app_password).
Bot side: incoming requests carry Authorization: Bearer <jwt> signed
by the channel; the bot validates against the channel's JWKS.
## Key Endpoints
Twin Plane (no auth):
GET /_twin/health
GET /_twin/scenarios
GET /_twin/settings
GET /_twin/references
POST /_twin/tenants
Twin Plane (Basic tenant_id:tenant_secret):
POST /_twin/accounts — kind=bot OR kind=bot_instance
GET /_twin/accounts — list both kinds
GET /_twin/logs
POST /_twin/simulate/inbound — drive a user→channel→bot delivery
GET /_twin/bots/<bot_id>/inbox — bot-half receive queue
POST /_twin/bots/<bot_id>/reply — operator-driven bot→channel reply
POST /_twin/feedback
GET /_twin/feedback
Channel half (no auth):
GET /v1/.well-known/openidconfiguration
GET /v1/.well-known/keys
POST /v1/.well-known/oauth2/v2.0/token (client_credentials)
Channel half (Bearer):
POST /v3/conversations/{conversationId}/activities
POST /v3/conversations/{conversationId}/activities/{activityId}
Bot half (Bearer JWT):
POST /api/messages
## Quick Start (local)
1. pip install twins-microsoft-bot-framework twins-microsoft-bot-framework-local
python -m twins_microsoft_bot_framework_local
2. Bootstrap a tenant:
curl -X POST http://localhost:8080/_twin/tenants \
-H "Content-Type: application/json" \
-d '{"friendly_name": "Dev"}'
# -> { tenant_id, tenant_secret }
3. Register a bot the channel will deliver to:
curl -X POST http://localhost:8080/_twin/accounts \
-u "TENANT_ID:TENANT_SECRET" \
-H "Content-Type: application/json" \
-d '{"kind":"bot","messaging_endpoint":"http://localhost:9000/api/messages"}'
# -> { app_id, app_password, ... }
4. Drive a synthetic user→channel→bot delivery:
curl -X POST http://localhost:8080/_twin/simulate/inbound \
-u "TENANT_ID:TENANT_SECRET" \
-H "Content-Type: application/json" \
-d '{"bot_app_id":"<APP_ID>","text":"hello"}'
# -> { activity, webhook: { webhook_delivered, webhook_url, reason, status_code } }
5. (Optional) point the same instance at itself to drive a round-trip
without external code: register a bot_instance with
trusted_openid_url=http://localhost:8080/v1/.well-known/openidconfiguration
and set the bot's messaging_endpoint to /api/messages on the same
process.
## Reference
GitHub: https://github.com/twins-la/microsoft-bot-framework
Project overview: https://twins.la
All twins: https://github.com/twins-la/twins-la