π Connect Your Own Bucket (Dashboard)
Keep your memories in a bucket you own - Amazon S3, Cloudflare R2, Supabase Storage, MinIO or any S3-compatible store - by connecting it from the dashboard. No code, and every MCP client on your account follows.
Why connect a bucket?β
By default the hosted service stores your memories on Lighthouse Storage (IPFS + Filecoin). Connect a bucket and your memories and their search index are written to that bucket instead - the hosted service keeps only your encrypted connection settings.
What changes and what does not:
| Default | With your bucket | |
|---|---|---|
| Where memories live | Lighthouse Storage | Your bucket |
| Where the search index lives | The hosted service | Your bucket |
forget | Stops renewal | Hard delete (the batch file goes once no other memory shares it) |
| Memory allowance | Your Lighthouse plan | Your Lighthouse plan - unchanged |
| MCP URL and client setup | - | Unchanged |
Running the SDK yourself instead of using the hosted service? Use Memory on S3 / R2 / MinIO - that is the same storage, wired in code.
Prerequisitesβ
- An account on the dashboard with no memories yet. Moving existing memories into a bucket is not supported yet, so a bucket can only be connected while you have none. Forget what you have first, or start with a new account.
- A saved Lighthouse API key (BYOK Keys). Your memory allowance still comes from your Lighthouse plan; until a key is saved, the memory tools stay locked.
- A bucket, and an access key that can read, write, list and delete objects in it.
- An endpoint the internet can reach over
https.localhost, private-network addresses and internal hostnames are refused, so MinIO on your own machine will not work here - use the SDK tutorial for that.
Step 1 - Create the bucket and a key limited to itβ
Create an empty, private bucket, then a key that can only reach that bucket.
| Provider | Where the key comes from |
|---|---|
| Amazon S3 | An IAM user's access key with the policy below. |
| Cloudflare R2 | R2 β Manage API tokens β a token with Object Read & Write, limited to this bucket. The page shows the Access Key ID, the Secret Access Key and the S3 API endpoint. |
| Supabase Storage | Your project's Storage settings β S3 connection. It shows the endpoint and region and creates the access keys. |
| MinIO / other | An access key with read, write, list and delete on the bucket. |
Smallest AWS policy that works (replace MY-BUCKET; lighthouse-memory is the default folder):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::MY-BUCKET/lighthouse-memory/*"
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::MY-BUCKET",
"Condition": { "StringLike": { "s3:prefix": "lighthouse-memory/*" } }
}
]
}
The Amazon S3 form in the dashboard shows this policy with your bucket and folder already filled in, ready to copy.
Step 2 - Open the formβ
In the dashboard go to API keys and find the card Where your memories are stored. Under Or use your own bucket, click Connect next to your provider.
| Provider | Fields |
|---|---|
| Amazon S3 | Bucket name Β· Region (e.g. us-east-1) |
| Cloudflare R2 | Bucket name Β· Account ID or S3 API endpoint (https://<account-id>.r2.cloudflarestorage.com) |
| Supabase Storage | Bucket name Β· Endpoint (https://<project-ref>.supabase.co/storage/v1/s3) Β· Region |
| MinIO or other S3-compatible | Endpoint (https://s3.example.com) Β· Bucket name Β· Region (optional, us-east-1 when empty) |
Every form also takes the Access key ID, the Secret access key, and an optional Folder in the bucket (lighthouse-memory when empty). Use the folder to share one bucket between projects.
Step 3 - Test, then connectβ
Test connection writes one small file under <folder>/connection-check/, reads it back, lists it and deletes it, then shows which of the four passed:
Connection works. β Write β Read β List β Delete
When one fails you get the reason and the step, for example a key that may not list:
Access denied. The key needs permission to read, write, delete and list objects in this bucket. β Write β Read β List β Delete
Connect bucket runs the same check and saves the settings only if it passes. The card then shows your bucket under In use:
my-memory-bucket/lighthouse-memory Β· us-east-1 Β· Key β’β’β’β’ABCD Β· 0 memories
Nothing is left behind by the check, and nothing is saved when it fails.
Step 4 - Use memory as usualβ
Nothing else changes. The dashboard and every MCP client signed in to your account write to the bucket from the next call on - same MCP URL, same client setup. memory_status confirms it (trimmed):
{
"engine": "batched",
"storage": "s3",
"namespace": "u-<your-user-id>-<8 hex>",
"memories": 12,
"pendingMemories": 2,
"flushEvery": 10,
"indexPath": "s3://my-memory-bucket/lighthouse-memory/index/u-<your-user-id>-<8 hex>.json"
}
Memories in the bucket count against your Lighthouse plan exactly like any others, and the usage shown in the dashboard includes them.
What is in your bucketβ
lighthouse-memory/
βββ blobs/<cid>.json one batch of memories (up to 10)
βββ names/mem-batch.<namespace>.<batchId>.json pointer to a batch, used for listing and rebuild
βββ index/<namespace>.json search index: text, tags and vectors of every memory
- A new memory is written to
index/straight away. It moves into a batch underblobs/when 10 are pending, or when you callmemory_flush. blobs/andnames/use the same layout as the SDK'ss3storage, so the SDK can read them (below).- Leave these files to the service. Editing or deleting them by hand makes the dashboard lose track of memories.
Everything in the bucket is plain, unencrypted JSON, including the text of your memories. Keep the bucket private and do not store secrets as memories.
Read the same bucket with the SDKβ
Your data stays readable without the hosted service. Flush first (memory_flush) so nothing is still pending, take namespace from memory_status, and point the SDK at the bucket:
# .env
S3_BUCKET=my-memory-bucket
S3_REGION=us-east-1
S3_PREFIX=lighthouse-memory # the folder from the form
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
MEMORY_NAMESPACE=u-... # "namespace" from memory_status
# R2 / MinIO / Supabase also need:
# S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
# S3_FORCE_PATH_STYLE=1
// read-bucket.mjs
import 'dotenv/config'
import '@lighthouse-ai/store-s3'
import '@lighthouse-ai/embed-keyword'
import { createStorage } from '@lighthouse-ai/core'
import { BatchedEngine } from '@lighthouse-ai/engine-batched'
const storage = await createStorage('s3', {})
const memory = new BatchedEngine(storage, { namespace: process.env.MEMORY_NAMESPACE, embedder: null })
console.log('rebuild:', await memory.rebuild()) // { restored: 12, source: 'blobs' }
console.log('list:', await memory.list({ limit: 5 }))
npm install @lighthouse-ai/core @lighthouse-ai/engine-batched @lighthouse-ai/store-s3 @lighthouse-ai/embed-keyword dotenv
npx tsx read-bucket.mjs
rebuild() re-reads every batch under names/ and builds a local index in .memory-sdk/. Treat this side as read-only: memories the SDK writes are not picked up by the dashboard's index.
Change keys, disconnect, reconnectβ
- Update keys - on the connected bucket. Same bucket and folder, new access key; use it when you rotate keys. It takes effect on the next call.
- Disconnect - you go back to Lighthouse Storage. If the bucket holds memories the dashboard asks first: they stay in your bucket, stop being searchable here, and stop counting against your plan.
- Reconnect - connect the same bucket and folder again and those memories come back and count again. This is refused if they no longer fit your plan.
- Another bucket or folder - disconnect first, then connect the new one. Memories are not moved between buckets.
Same thing over the APIβ
The dashboard calls four endpoints on the memory API. API=https://memory-api.lighthouse.storage, and $JWT is a dashboard session token (as in BYOK Keys).
# Where do my memories go?
curl $API/v1/storage -H "authorization: Bearer $JWT"
# { "ok": true, "available": true, "defaultStorage": "lh-ipfs-filecoin", "connected": false }
# Check settings without saving them
curl -X POST $API/v1/storage/test \
-H "authorization: Bearer $JWT" -H "content-type: application/json" \
-d '{"provider":"s3","bucket":"my-memory-bucket","region":"us-east-1","accessKeyId":"AKIA...","secretAccessKey":"..."}'
# 200 { "ok": true, "checks": { "write": true, "read": true, "list": true, "delete": true } }
# 400 { "ok": false, "checks": { "write": true, "read": true, "list": false, "delete": false },
# "step": "list", "code": "AccessDenied", "error": "Access denied. The key needs permission to read, write, delete and list objects in this bucket." }
# Check, then save (same body as the test)
curl -X PUT $API/v1/storage \
-H "authorization: Bearer $JWT" -H "content-type: application/json" \
-d '{"provider":"s3","bucket":"my-memory-bucket","region":"us-east-1","accessKeyId":"AKIA...","secretAccessKey":"..."}'
# 200 { "ok": true, "connected": true, "provider": "s3", "bucket": "my-memory-bucket", "region": "us-east-1",
# "endpoint": "", "prefix": "lighthouse-memory", "forcePathStyle": false, "accessKeyLast4": "ABCD",
# "memories": 0, "updatedAt": "β¦", "adopted": 0 }
# 400 { "ok": false, "error": "Enter the bucket's AWS region, e.g. us-east-1.", "field": "region" }
# 409 { "ok": false, "error": "You already have 3 memories on Lighthouse Storage. β¦", "memories": 3 }
# Disconnect
curl -X DELETE $API/v1/storage -H "authorization: Bearer $JWT"
# 200 { "ok": true, "removed": true, "memories": 0, "released": 0, "defaultStorage": "lh-ipfs-filecoin" }
# 409 { "ok": false, "error": "This bucket holds 12 memories. β¦", "memories": 12, "reachable": true }
curl -X DELETE "$API/v1/storage?force=true" -H "authorization: Bearer $JWT"
# 200 { "ok": true, "removed": true, "memories": 12, "released": 12, "defaultStorage": "lh-ipfs-filecoin" }
Body per provider (all take an optional prefix, the folder):
provider | Fields besides bucket, accessKeyId, secretAccessKey |
|---|---|
s3 | region |
r2 | accountId or endpoint |
supabase | endpoint, region |
custom | endpoint; optional region, forcePathStyle (default true) |
adopted is how many memories the bucket already held when you connected it. POST /v1/storage/test with an empty body re-checks the saved connection. Tests and saves are limited to 30 per 10 minutes.
There is no MCP tool for any of this, on purpose: bucket keys never pass through an AI chat.
How your keys are handledβ
- Sent once over HTTPS to the memory API, checked against your bucket, then stored encrypted. They are never returned - only the last 4 characters of the access key ID are shown.
- Used only to reach the bucket you named. Give the key access to that bucket and nothing else.
- Removed when you disconnect.
Troubleshootingβ
| Message | Fix |
|---|---|
You already have N memories on Lighthouse Storage⦠| A bucket can only be connected with no memories. Forget them first, or use a new account. |
Bucket not found. Check the bucket name and the region or endpoint. | Typo in the bucket name, or the wrong region / endpoint for it. |
Access denied. The key needs permission to read, write, delete and list⦠| The ticks show which permission is missing. On AWS, compare with the policy above - s3:ListBucket is the usual one. |
That access key ID was not recognised. / The secret access key does not match⦠| Re-copy the pair. On R2, use the S3 credentials shown when the token was created, not the token value. |
The bucket is in a different region. Check the region. | Use the region the bucket was created in. |
That endpoint points to a private or internal address⦠| The endpoint must be public. A local MinIO needs the SDK, not the dashboard. |
The endpoint must start with https:// | Plain http endpoints are refused. |
The endpoint's TLS certificate could not be verified. | Use a certificate from a public authority; self-signed certificates are refused. |
Too many attempts. Wait a few minutes and try again. | 30 tests and saves per 10 minutes. |
| Connected, but the agent only sees three key tools | No Lighthouse key saved yet - see BYOK Keys. |
| Memory calls fail after it worked | The key was revoked or the bucket removed. Fix it with Update keys, or disconnect. |
Next stepsβ
- Memory on S3 / R2 / MinIO - the same storage from your own code.
- BYOK Keys - the Lighthouse key your memory allowance comes from.
- MCP Overview - connect agents to the hosted server.