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:

Manual-trust requirement. Real Bot Framework SDKs hardcode 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

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