S3 and DynamoDB in the cloud
In this setup there is no git repository behind the board. The Kanbus API service runs as an AWS Lambda function and stores the board in a private S3 bucket, with a DynamoDB table as a query index. A web application signs users in and renders the board with the shared Kanbus UI. This is how you put a Kanbus board inside a product, for people who will never run kbs.
flowchart TB
browser[Browser: your app with @kanbus/ui]
gateway[HTTP API with JWT authorizer]
lambda[Kanbus API Lambda]
s3[(S3 bucket: canonical issues, events, .kanbus.yml)]
ddb[(DynamoDB table: catalog for queries)]
browser -- signed-in requests --> gateway
gateway --> lambda
lambda --> s3
lambda --> ddb
S3 holds the same files a git checkout would: .kanbus.yml, project/issues/, and project/events/, under a tenant/project prefix. DynamoDB holds only a catalog that makes filtering fast; it can be rebuilt from S3 at any time.
The service is in services/kanbus-api. Its full contract is in the service README.
What the API supports today
- Create, read, update, and delete issues; add comments; read an issue's event history.
- Filter by project, type, status, assignee, label, parent, text search, and custom fields.
- Optimistic concurrency: every edit sends the revision it last read and fails with a conflict if someone changed the issue in between.
- Safe retries: every write carries an operation id, and repeating it does not apply the change twice.
It does not yet support setting or changing parents, dependency links, or deleting issues that have children. Projects with policy files or lifecycle hooks configured are refused rather than run without them. A deployment serves at most 8 projects, and a single query may consider at most 1,000 issues, so large boards need narrow filters.
1. Try the API on your machine
The same API runs locally against a folder, which is the quickest way to see how it behaves. You need the Rust toolchain and a Kanbus board, such as the one from the local files guide.
git clone https://github.com/AnthusAI/Kanbus.git
cd Kanbus/services/kanbus-api
cargo build --bin kanbus-api-local
Start it, pointing KANBUS_ROOT at the folder that contains .kanbus.yml:
export KANBUS_BACKEND=file
export KANBUS_ROOT=/path/to/your/repository
export KANBUS_TENANT=acme
export KANBUS_PROJECT=website
export KANBUS_LOCAL_TOKEN=choose-a-secret-of-16-or-more-characters
target/debug/kanbus-api-local
# Kanbus API listening at http://127.0.0.1:5175
The local server listens on 127.0.0.1 only and accepts one bearer token. It keeps write bookkeeping in a sibling folder named .<folder>.virtuus.
In another terminal, read the board and make changes:
API=http://127.0.0.1:5175
AUTH="Authorization: Bearer choose-a-secret-of-16-or-more-characters"
JSON="Content-Type: application/json"
curl -s $API/acme/website/api/config -H "$AUTH"
curl -s "$API/acme/website/api/issues?status=open" -H "$AUTH"
curl -s -X POST $API/acme/website/api/issues -H "$AUTH" -H "$JSON" \
-H "X-Operation-Id: $(uuidgen)" \
-d '{"title":"Created through the API","type":"bug"}'
# {"issue":{"id":"acme-6b3a9b94-...", ...},"revision":"a5cd...","catalog_pending":false}
Edits, comments, and deletes also need the revision from the last read, in If-Match:
curl -s -X PATCH $API/acme/website/api/issues/acme-6b3a9b94-b9ef-41ff-a083-315278fd8cd4 \
-H "$AUTH" -H "$JSON" -H "X-Operation-Id: $(uuidgen)" -H "If-Match: a5cd..." \
-d '{"status":"in_progress"}'
curl -s -X POST $API/acme/website/api/issues/acme-6b3a9b94-b9ef-41ff-a083-315278fd8cd4/comments \
-H "$AUTH" -H "$JSON" -H "X-Operation-Id: $(uuidgen)" -H "If-Match: <new revision>" \
-d '{"text":"Seen in production."}'
Sending an old revision returns 409 with revision_conflict; read the issue again and retry. The files the API writes are ordinary Kanbus files, so kbs list in the same folder shows them.
The API reads the folder once when it starts. While it runs, it must be the only writer: changes made with kbs in the same folder do not appear in API queries until you restart it.
2. Deploy into an Amplify application
The API is not a separate product to sign up for. You add it to an existing Amplify Gen 2 application's backend, which then owns the bucket, table, function, and HTTP API, and deploys them with the rest of the app. The step-by-step reference is Embed a cloud Kanbus board in an Amplify application; Prospectus is a working example. In summary:
-
Build the Lambda. Pin a Kanbus commit and build the
kanbus-api-lambdabinary with--features awsforx86_64-unknown-linux-musl, using the Rust version inservices/kanbus-api/rust-toolchain.toml. Zip it asbootstrap. -
Declare the resources in a CDK stack from
backend.createStack(...):- a private, versioned S3 bucket that blocks public access, requires TLS, and is retained on removal;
- a DynamoDB table with partition key
scopeand sort keypath(both strings), on-demand capacity, point-in-time recovery, and theby_type,by_status, andby_type_statusindexes; - the Lambda (
PROVIDED_AL2023, handlerbootstrap,X86_64); - an HTTP API with a JWT authorizer for your app's user pool, routing
/{tenant}/{project}/api/{proxy+}and/{tenant}/api/issuesfor GET, POST, PATCH, and DELETE.
-
Configure the Lambda:
KANBUS_BACKEND=s3 KANBUS_OBJECT_BUCKET=<bucket name> KANBUS_CATALOG_TABLE=<table name>Signing in is not enough to see a board; users must be in a Cognito group named
kanbus:acme/website(one board) orkanbus:acme/*(every board of the tenant). One deployment serves every board; a new board needs no redeploy. -
Scope permissions to the namespace: S3 access to
acme/website/*and_virtuus/acme/website/*, and DynamoDB access withLeadingKeysset toacme/website. The reference guide lists the exact conditions. -
Publish
.kanbus.ymltoacme/website/.kanbus.ymlin the bucket, from a reviewed file in your app's source, for example with aBucketDeploymentthat hasprune: false. -
Allow your app's origin in CORS, with the
authorization,content-type,if-match, andx-operation-idheaders, and exposeetag.
Deploy through the app's normal Amplify pipeline.
3. Bring in an existing board
To move a board from git into S3, stop all writers, copy .kanbus.yml and the project folder (project/issues/, project/events/) into the namespace prefix unchanged, and rebuild the DynamoDB catalog with Virtuus before you let anyone write. S3 stays the source of truth; rebuilding the catalog never rewrites issue files.
4. Render the board
Install @kanbus/ui from the tarball attached to the Kanbus GitHub release (it is not on npm) and render IssueCollection with the configuration from GET /{tenant}/{project}/api/config and the issues from GET /{tenant}/{project}/api/issues. Your app handles sign-in, sends the user's token on each request, generates a new operation id for each user action, and keeps each card's revision for edits. Show revision conflicts to the user rather than retrying silently. See the UI package README.
5. Check the deployment
Before you let staff write to it, check in a test namespace that:
- requests without a token, and from signed-in users without a
kanbus:group for the board, are refused; - a group member can create, reload, edit, and comment on an issue;
- an edit with an old revision returns a conflict;
- retrying a write with the same operation id does not add a second event;
- rebuilding the catalog restores search results without changing any S3 object.
Next
- Board configuration for your domain (types, statuses, custom fields): Configuration.