Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6a966f9
Make Repository interface multi-bot aware
dahlia Jul 4, 2026
e5b4c1b
Migrate legacy KV data to bot-scoped keys
dahlia Jul 4, 2026
a6d775d
Migrate legacy SQLite databases to bot_id schema
dahlia Jul 4, 2026
45a6e65
Migrate legacy PostgreSQL schemas to bot_id layout
dahlia Jul 4, 2026
51bd6a9
Carry bot identifiers in local object URIs
dahlia Jul 4, 2026
0da77e2
Redirect legacy object URIs to canonical ones
dahlia Jul 4, 2026
c909fd4
Expose a read-only bot view through Session.bot
dahlia Jul 4, 2026
8362285
Separate the Instance from the Bot
dahlia Jul 4, 2026
3a087d5
Adopt legacy repository data on createBot() startup
dahlia Jul 4, 2026
6226cf8
Host multiple static bots with an instance actor
dahlia Jul 4, 2026
8c951e2
Route shared-inbox activities to relevant bots
dahlia Jul 4, 2026
e836167
Serve per-bot web pages under handle paths
dahlia Jul 4, 2026
b8662e7
Support dynamic bots resolved on demand
dahlia Jul 4, 2026
63674ed
Expose the instance module as a package entry
dahlia Jul 4, 2026
7434d1c
Document multi-bot instances
dahlia Jul 4, 2026
b6cc6b9
Cover sessions and quick start in multi-bot docs
dahlia Jul 4, 2026
cbdf001
Spell out dispatcher fallthrough for dynamic bots
dahlia Jul 4, 2026
89cce6d
Document hosting multiple bot groups side by side
dahlia Jul 4, 2026
7942311
Make the instance actor identifier configurable
dahlia Jul 4, 2026
1b8a563
Document the instanceActorIdentifier option
dahlia Jul 4, 2026
6906667
Keep the docs build out of the Node.js test gate
dahlia Jul 4, 2026
393d19a
Require context data in BotGroup.getSession()
dahlia Jul 4, 2026
f230e5d
Avoid an any cast in the multi-app dispatch
dahlia Jul 4, 2026
ae50d4c
Forward legacy migration through cached repositories
dahlia Jul 4, 2026
f3a70e5
Emit custom page CSS without HTML escaping
dahlia Jul 5, 2026
08999bf
Memoize instance actor key generation in process
dahlia Jul 5, 2026
e1323d5
Close the emoji file handle on failed responses
dahlia Jul 5, 2026
6519934
Serialize the PostgreSQL legacy schema upgrade
dahlia Jul 5, 2026
e7deaa8
Cover cross-bot update and delete rejection
dahlia Jul 5, 2026
f609c6a
Migrate legacy KV data eagerly under a claimed marker
dahlia Jul 5, 2026
c4b57a3
Ignore likes of other bots' local objects
dahlia Jul 5, 2026
1251d82
Read custom emojis into memory when serving them
dahlia Jul 5, 2026
16a0890
Match static bot usernames case-insensitively
dahlia Jul 5, 2026
6fe1533
Publish the core peer dependency as a caret range
dahlia Jul 5, 2026
abb1831
Point hashtag links at bot-scoped tag pages
dahlia Jul 5, 2026
da6b208
Share the mtime fallback between ETag and Last-Modified
dahlia Jul 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ Architecture
- *src/mod.ts* - Main entry point, re-exports all public APIs
- *src/bot.ts* - Core Bot interface and createBot function
- *src/bot-impl.ts* - Internal Bot implementation
- *src/instance.ts* - Instance interface and createInstance function for
hosting multiple bots on a single server
- *src/instance-impl.ts* - Internal Instance implementation, which owns
the Fedify federation and routes incoming activities to the right bots
- *src/session.ts* - Session management for bot operations
- *src/message.ts* - Message types and ActivityPub objects (Note, Article,
etc.)
Expand All @@ -80,8 +84,13 @@ Architecture

### Key concepts

- *Bot*: The main bot instance created with `createBot()`, handles events
and provides session access
- *Instance*: A server hosting one or more bots, created with
`createInstance()`; owns the shared infrastructure (KV store, queue,
repository, HTTP handling)
- *Bot*: An individual ActivityPub actor created with `createBot()` (which
hosts a single bot on a dedicated instance) or `Instance.createBot()`
(static bots or dynamic bot groups); handles events and provides session
access
- *Session*: Scoped bot operations for publishing content and managing state
- *Message*: ActivityPub objects like Note, Article, Question with rich text
support
Expand Down
97 changes: 97 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,103 @@ Version 0.5.0

To be released.

### @fedify/botkit

- Added support for hosting multiple bots on a single instance.
[[#16], [#24]]

The new `createInstance()` function creates an *instance* that owns the
shared infrastructure (the key–value store, the message queue, the
repository, and HTTP handling), on which multiple bots can be hosted,
each with its own actor identity and event handlers.

- Added `createInstance()` function.
- Added `Instance` interface.
- Added `InstanceWithVoidContextData` interface.
- Added `CreateInstanceOptions` interface.
- Added `Instance.createBot()` method, which creates a static bot from
an identifier and a `BotProfile`, or a dynamic `BotGroup` from
a `BotDispatcher` function that resolves bots on demand (e.g. one
bot per region, backed by a database).
- Added `BotProfile` interface.
- Added `BotDispatcher` type.
- Added `BotGroup` interface.
- Added `CreateBotGroupOptions` interface, whose `mapUsername` option
resolves WebFinger usernames to dynamic bot identifiers.
- Added `BotEventHandlers` interface, which `Bot` and `BotGroup` both
extend.
- Added `DEFAULT_INSTANCE_ACTOR_IDENTIFIER` constant. Multi-bot
instances expose an instance actor under a reserved identifier,
whose key signs shared-inbox related requests; it can be overridden
through the `CreateInstanceOptions.instanceActorIdentifier` option.
- Added `@fedify/botkit/instance` module.

Activities delivered to the shared inbox are routed to the bots they are
relevant to: the followed or unfollowed bot, the owner of the liked or
replied-to message, mentioned bots, addressed bots, and bots following
the author. Multi-bot instances serve a bot list at the web root and
each bot's pages under `/@{username}`.

The existing `createBot()` function keeps working for single-bot
deployments and preserves their behavior, including the web pages served
at the root.

- The `Repository` interface now stores data for multiple bot actors:
every method takes the identifier of the owning bot actor as its first
parameter, and data belonging to different identifiers are isolated from
each other. This is a breaking change for custom `Repository`
implementations. [[#16], [#24]]

- Added `identifier` parameter to all `Repository` methods.
- Added `Repository.findFollowedBots()` method, a reverse lookup
answering which bots follow a given actor.
- Added optional `Repository.migrate()` method for adopting data
stored by BotKit 0.4 or earlier.
- Added `Repository.forIdentifier()` method and `ActorScopedRepository`
class, a view of a repository bound to a single bot actor.
- `KvRepository` now stores data under bot-scoped keys. Its second
constructor parameter is now a `KvRepositoryOptions` object with
a single `prefix` option, replacing the removed
`KvStoreRepositoryPrefixes` interface.
- `createBot()` migrates data stored by BotKit 0.4 or earlier to the
bot-scoped layout on startup.

- Local object URIs now carry the identifier of the owning bot actor,
e.g. `/ap/actor/{identifier}/note/{id}` instead of `/ap/note/{id}`.
URIs in the old format are still recognized in incoming activities and
are permanently redirected to their canonical URIs when dereferenced,
so links stored by remote servers keep working after an upgrade.
[[#16], [#24]]

- The `Session.bot` property is now typed as `ReadonlyBot`, a read-only
view of the bot's identity and profile, instead of `Bot`. This is
a breaking change for code that reached the full `Bot` through
a session; such code should hold on to the `Bot` returned by
`createBot()` instead. [[#16], [#24]]

- Added `ReadonlyBot` interface.

[#16]: https://github.com/fedify-dev/botkit/issues/16
[#24]: https://github.com/fedify-dev/botkit/pull/24

### @fedify/botkit-sqlite

- All tables now have a `bot_id` column and composite primary keys, so
a single database stores the data of multiple bots. Opening a database
created by version 0.4 or earlier rebuilds the affected tables in place,
and `SqliteRepository.migrate()` assigns the carried-over rows to a bot
actor identifier; `createBot()` calls it automatically on startup.
[[#16], [#24]]

### @fedify/botkit-postgres

- All tables now have a `bot_id` column and composite primary keys, so
a single schema stores the data of multiple bots. Initializing a schema
created by version 0.4 upgrades it in place, and
`PostgresRepository.migrate()` assigns the carried-over rows to a bot
actor identifier; `createBot()` calls it automatically on startup.
[[#16], [#24]]


Version 0.4.3
-------------
Expand Down
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"fmt": "deno fmt && deno task hongdown --write",
"install": "deno cache packages/*/src/*.ts && pnpm install",
"test": "deno test --allow-read --allow-write --allow-env --allow-net=hollo.social,localhost,127.0.0.1 --parallel",
"test:node": "pnpm install && pnpm run -r test",
"test:node": "pnpm install && pnpm --filter './packages/*' run build && pnpm run -r test",
"test-all": {
"dependencies": ["check", "test", "test:node"]
},
Expand Down
1 change: 1 addition & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const concepts = {
text: "Concepts",
items: [
{ text: "Bot", link: "/concepts/bot.md" },
{ text: "Instance", link: "/concepts/instance.md" },
{ text: "Session", link: "/concepts/session.md" },
{ text: "Events", link: "/concepts/events.md" },
{ text: "Message", link: "/concepts/message.md" },
Expand Down
5 changes: 5 additions & 0 deletions docs/concepts/bot.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ BotKit also exposes the bot actor's standard ActivityPub collections. The
JSON-LD responses of the `outbox` and `followers` collections include the
[FEP-5711] inverse properties `outboxOf` and `followersOf`.

> [!TIP]
> A bot created by `createBot()` occupies its whole server. Since BotKit
> 0.5.0, a single server can also host multiple bots; see the
> [*Instance* concept document](./instance.md).

[FEP-5711]: https://w3id.org/fep/5711


Expand Down
Loading
Loading