Source & license
Upstream license: AGPL-3.0-only
License TL;DR
You can use and change it, even commercially. If people use your modified version over a network, offer them its corresponding source under the AGPL. Sharing copies has source-sharing duties too. Sharing source code is different from sharing users’ content.
Explain AGPL v3 in plain English →Summary of the main license. Separate packages and assets can have different terms.
Inspect repository ↗Read this project’s actual license ↗Repository owner
See the upstream repository for the original creator and contributors.
Maintain this project? Maintainer verification →Cloudflare hosting
Free tier eligible within limits
The documented FlareMo deployment can use Cloudflare Free allowances for a small workload under the request, CPU and service-specific quotas below. This is conditional eligibility, not a measured zero-cost deployment; optional features, domains and external providers can add costs.
Hosting requirements
- Workers Free allows 100,000 requests per day shared across the account and 10 ms CPU per invocation; measure CPU-heavy authentication, parsing and rendering before assuming it fits.
- Keep aggregate D1 use below 5 million rows read/day, 100,000 rows written/day and 5 GB total storage; a request can touch many rows.
- Use R2 Standard storage, at most 10 GB-month, 1 million Class A operations and 10 million Class B operations/month; provision an eligible billing-enabled R2 account.
- Keep total queue operations within 10,000/day, counting writes, reads, deletes, retries and each 64 KB chunk; Free retention is 24 hours.
- Only ordinary Free-eligible Workers AI models are covered, within 10,000 neurons/day; optional external providers and paid-only models are excluded.
- Keep both indexes combined within 5 million stored dimensions and 30 million queried dimensions/month; dimensions, not note counts, determine Vectorize usage.
- Use a small personal or team workload; domain registration and optional third-party providers are separate costs. Provision your own IDs, secrets and migrations.
Sources checked 01/10/2026
Repository snapshot: 5f43c8e. Hosting eligibility reflects the deployment documentation and listed assumptions.
- memos ↗
FlareMo answers a simpler question: **Can you get a 24/7 online, resilient, globally accelerated knowledge base with zero server maintenance, using just a free
- workers ↗
{ "$schema": "./node_modules/wrangler/config-schema.json", "name": "flaremo", "main": "./apps/worker/src/index.ts", "compatibility_date": "2026-07-10", "compatibility_flags": ["nodejs_compat"], "observability": { "enabled": true, "head_sampling_rate": 1 }, "assets": { "directory": "./apps/web/dist", "binding": "ASSETS", "not_found_handling": "single-page-application", "run_worker_first": [
- d1 ↗
", "/share/*", "/sitemap.xml", "/sitemap-articles.xml", "/favicon.ico" ] }, "d1_databases": [ { "binding": "DB", "database_name": "flaremo", "database_id": "00000000-0000-0000-0000-000000000000", "migrations_dir": "./migrations" } ], "r2_buckets": [ { "binding": "ATTACHMENTS", "bucket_name": "flaremo-attachments" } ], "queues": { "producers": [ { "binding": "MEMBER_REMOVAL_QUEUE",
- r2 ↗
"database_id": "00000000-0000-0000-0000-000000000000", "migrations_dir": "./migrations" } ], "r2_buckets": [ { "binding": "ATTACHMENTS", "bucket_name": "flaremo-attachments" } ], "queues": { "producers": [ { "binding": "MEMBER_REMOVAL_QUEUE", "queue": "flaremo-member-removal" }, { "binding": "DATA_EXPORT_QUEUE", "queue": "flaremo-data-export" } ], "consumers": [ { "queue": "flaremo-mem
- queues ↗
{ "binding": "ATTACHMENTS", "bucket_name": "flaremo-attachments" } ], "queues": { "producers": [ { "binding": "MEMBER_REMOVAL_QUEUE", "queue": "flaremo-member-removal" }, { "binding": "DATA_EXPORT_QUEUE", "queue": "flaremo-data-export" } ], "consumers": [ { "queue": "flaremo-member-removal", "max_batch_size": 10, "max_retries": 5 }, { "queue": "flaremo-data-export", "max
- workers-ai ↗
ame": "flaremo-memos" }, { "binding": "VECTORIZE_MEMORIES", "index_name": "flaremo-memories" } ], "ai": { "binding": "AI" }, "ratelimits": [ { "name": "RATE_LIMITER", "namespace_id": "1001", "simple": { "limit": 30, "period": 60 } } ], "triggers": { "crons": ["17 3 * * *"] }, "vars": { "FLAREMO_DEPLOY_REPOSITORY": "", "FLAREMO_SINGLE_USER_EMAIL": "owner@flaremo.local", "FLAREMO_SINGLE_USER_NAME": "FlareMo Owner",
- vectorize ↗
"queue": "flaremo-data-export", "max_batch_size": 5, "max_retries": 5 } ] }, "vectorize": [ { "binding": "VECTORIZE_MEMOS", "index_name": "flaremo-memos" }, { "binding": "VECTORIZE_MEMORIES", "index_name": "flaremo-memories" } ], "ai": { "binding": "AI" }, "ratelimits": [ { "name": "RATE_LIMITER", "namespace_id": "1001", "simple": { "limit": 30, "period": 60 } } ], "triggers": { "crons": ["17
- free-tier-eligible ↗
{ "$schema": "./node_modules/wrangler/config-schema.json", "name": "flaremo", "main": "./apps/worker/src/index.ts", "compatibility_date": "2026-07-10", "compatibility_flags": ["nodejs_compat"], "observability": { "enabled": true, "head_sampling_rate": 1 }, "assets": { "directory": "./apps/web/dist", "binding": "ASSETS", "not_found_handling": "single-page-application", "run_worker_first": [
- free-tier-eligible ↗
", "/share/*", "/sitemap.xml", "/sitemap-articles.xml", "/favicon.ico" ] }, "d1_databases": [ { "binding": "DB", "database_name": "flaremo", "database_id": "00000000-0000-0000-0000-000000000000", "migrations_dir": "./migrations" } ], "r2_buckets": [ { "binding": "ATTACHMENTS", "bucket_name": "flaremo-attachments" } ], "queues": { "producers": [ { "binding": "MEMBER_REMOVAL_QUEUE",
- free-tier-eligible ↗
"database_id": "00000000-0000-0000-0000-000000000000", "migrations_dir": "./migrations" } ], "r2_buckets": [ { "binding": "ATTACHMENTS", "bucket_name": "flaremo-attachments" } ], "queues": { "producers": [ { "binding": "MEMBER_REMOVAL_QUEUE", "queue": "flaremo-member-removal" }, { "binding": "DATA_EXPORT_QUEUE", "queue": "flaremo-data-export" } ], "consumers": [ { "queue": "flaremo-mem
- free-tier-eligible ↗
up>1, 2, 3, 4</sup> | Duration | CPU time | | --- | --- | --- | --- | | **Free** | 100,000 per day | No charge for duration | 10 milliseconds of CPU time per invocation | | **Standard** | 10 million included per month <br> +$0.30 per additional million | No charge or limit for duration | 30 million CPU milliseconds included per month<br> +$0.02 per additional million CPU milliseconds<br><br> Max of [5 minutes of CPU time](https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits) per invocation (default: 30 seconds)<br> Max of 15 minutes of CPU time per [Cron Trigger](https://developers.cloudflare.com/workers/configuration/cron-triggers/) or [Queue Consumer](https://developers.cloudflare.co
- free-tier-eligible ↗
oudflare.com/workers/platform/pricing/#workers) | | --- | --- | --- | | Rows read | 5 million / day | First 25 billion / month included + $0.001 / million rows | | Rows written | 100,000 / day | First 50 million / month included + $1.00 / million rows | | Storage (per GB stored) | 5 GB (total) | First 5 GB included + $0.75 / GB-mo | Track your D1 usage To accurately track your usage, use the [meta object](https://developers.cloudflare.com/d1/worker-api/return-object/), [GraphQL Analytics API](https://developers.cloudflare.com/d1/observability/metrics-analytics/#query-via-the-graphql-api), or the [Cloudflare dashboard ↗︎](https://dash.cloudflare.com/?to=/:account/workers/d1/). Select your D1 database, then vie
- free-tier-eligible ↗
infrequent access storage) for 1.1 GB, you will be billed for 2 GB. ### Free tier You can use the following amount of storage and operations each month for free. | | Free | | --- | --- | | Storage | 10 GB-month / month | | Class A Operations | 1 million requests / month | | Class B Operations | 10 million requests / month | | Egress (data transfer to Internet) | Free <sup>[1](#user-content-fn-1)</sup> | Caution The free tier only applies to Standard storage, and does not apply to Infrequent Access storage. ### Storage usage Storage is billed using gigabyte-month (GB-month) as the billing metric. A GB-month is calculated by averaging the *peak* storage per day over a billing period (30 days). For examp
- free-tier-eligible ↗
dth) charges. | | Workers Free | Workers Paid | | --- | --- | --- | | Standard operations | 10,000 operations/day included | 1,000,000 operations/month included + $0.40/million operations | | Message retention | 24 hours (non-configurable) | 4 days default, configurable up to 14 days | In most cases, it takes 3 operations to deliver a message: 1 write, 1 read, and 1 delete. Therefore, you can use the following formula to estimate your monthly bill: ```txt ((Number of Messages * 3) - 1,000,000) / 1,000,000 * $0.40 ``` Additionally: - Each retry incurs a read operation. A batch of 10 messages that is retried would incur 10 operations for each retry. - Messages that reach the maximum retries and that are wr
- free-tier-eligible ↗
000 Neurons**. Our free allocation allows anyone to use a total of **10,000 Neurons per day at no charge**. To use more than 10,000 Neurons per day, you need to sign up for the [Workers Paid plan](https://developers.cloudflare.com/workers/platform/pricing/#workers). On Workers Paid, you will be charged at $0.011 / 1,000 Neurons for any usage above the free allocation of 10,000 Neurons per day. You can monitor your Neuron usage in the [Cloudflare Workers AI dashboard ↗︎](https://dash.cloudflare.com/?to=/:account/ai/workers-ai). All limits reset daily at 00:00 UTC. If you exceed any one of the above limits, further operations will fail with an error. | | Free <br> allocation | Pricing | | --- | --- | --- | |
- free-tier-eligible ↗
oudflare.com/workers/platform/pricing/#workers) | | --- | --- | --- | | **Total queried vector dimensions** | 30 million queried vector dimensions / month | First 50 million queried vector dimensions / month included + $0.01 per million | | **Total stored vector dimensions** | 5 million stored vector dimensions | First 10 million stored vector dimensions + $0.05 per 100 million | ### Calculating vector dimensions To calculate your potential usage, calculate the queried vector dimensions and the stored vector dimensions, and multiply by the unit price. The formula is defined as `((queried vectors + stored vectors) * dimensions * ($0.01 / 1,000,000)) + (stored vectors * dimensions * ($0.05 / 100,000,000))` - F
- AGPL-3.0-only ↗
GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a progr
- architecture ↗
{ "$schema": "./node_modules/wrangler/config-schema.json", "name": "flaremo", "main": "./apps/worker/src/index.ts", "compatibility_date": "2026-07-10", "compatibility_flags": ["nodejs_compat"], "observability": { "enabled": true, "head_sampling_rate": 1 }, "assets": { "directory": "./apps/web/dist", "binding": "ASSETS", "not_found_handling": "single-page-application", "run_worker_first": [
- architecture ↗
", "/share/*", "/sitemap.xml", "/sitemap-articles.xml", "/favicon.ico" ] }, "d1_databases": [ { "binding": "DB", "database_name": "flaremo", "database_id": "00000000-0000-0000-0000-000000000000", "migrations_dir": "./migrations" } ], "r2_buckets": [ { "binding": "ATTACHMENTS", "bucket_name": "flaremo-attachments" } ], "queues": { "producers": [ { "binding": "MEMBER_REMOVAL_QUEUE",
- architecture ↗
"database_id": "00000000-0000-0000-0000-000000000000", "migrations_dir": "./migrations" } ], "r2_buckets": [ { "binding": "ATTACHMENTS", "bucket_name": "flaremo-attachments" } ], "queues": { "producers": [ { "binding": "MEMBER_REMOVAL_QUEUE", "queue": "flaremo-member-removal" }, { "binding": "DATA_EXPORT_QUEUE", "queue": "flaremo-data-export" } ], "consumers": [ { "queue": "flaremo-mem
- architecture ↗
{ "binding": "ATTACHMENTS", "bucket_name": "flaremo-attachments" } ], "queues": { "producers": [ { "binding": "MEMBER_REMOVAL_QUEUE", "queue": "flaremo-member-removal" }, { "binding": "DATA_EXPORT_QUEUE", "queue": "flaremo-data-export" } ], "consumers": [ { "queue": "flaremo-member-removal", "max_batch_size": 10, "max_retries": 5 }, { "queue": "flaremo-data-export", "max
- architecture ↗
ame": "flaremo-memos" }, { "binding": "VECTORIZE_MEMORIES", "index_name": "flaremo-memories" } ], "ai": { "binding": "AI" }, "ratelimits": [ { "name": "RATE_LIMITER", "namespace_id": "1001", "simple": { "limit": 30, "period": 60 } } ], "triggers": { "crons": ["17 3 * * *"] }, "vars": { "FLAREMO_DEPLOY_REPOSITORY": "", "FLAREMO_SINGLE_USER_EMAIL": "owner@flaremo.local", "FLAREMO_SINGLE_USER_NAME": "FlareMo Owner",
- architecture ↗
"queue": "flaremo-data-export", "max_batch_size": 5, "max_retries": 5 } ] }, "vectorize": [ { "binding": "VECTORIZE_MEMOS", "index_name": "flaremo-memos" }, { "binding": "VECTORIZE_MEMORIES", "index_name": "flaremo-memories" } ], "ai": { "binding": "AI" }, "ratelimits": [ { "name": "RATE_LIMITER", "namespace_id": "1001", "simple": { "limit": 30, "period": 60 } } ], "triggers": { "crons": ["17
Upstream screenshot · realchendahuang/FlareMo repository contributors ↗. Depicts the upstream project. We have not deployed and tested a fresh installation here.
What it can replace
Compare the workflow you need. These mappings describe overlap; full feature parity requires a separate comparison.
Notes and memos with tagging, search, timeline, attachments and import; complete Memos API compatibility and all managed collaboration features are excluded.
See supporting source ↗How it works
The shape of FlareMo on Cloudflare, and how it stacks up against the rented tools it replaces.
Architecture
Diagram of deployment declarations at the reviewed commit. Each app has its own entrypoint; declared resources do not prove runtime calls. Follow file and line sources below.
View upstream source ↗Configuration and workflow sources
Reviewed commit 5f43c8e067a3. Files were read as data; upstream applications and CI jobs were not executed.
Partial source coverage: 34 files outside collection bounds; 0 collection or parsing issues. Dynamic imports and generated entrypoints may need manual review.
Deployment configuration · 3 files
Cloudflare Workers · compatibility 2026-07-10
flaremo · default
Entrypoint: ./apps/worker/src/index.ts
Static assets: ./apps/web/dist · single-page-application · Worker first: ["/api/*","/article/*","/feed.xml","/file/*","/mcp","/memory/*","/openapi.json","/memos.api.v1.*","/share/*","/sitemap.xml","/sitemap-articles.xml","/favicon.ico"]
Cron triggers (UTC): 17 3 * * *
DB→ D1ATTACHMENTS→ R2AI→ Workers AIVECTORIZE_MEMOS→ VectorizeVECTORIZE_MEMORIES→ VectorizeMEMBER_REMOVAL_QUEUE→ Queues (producer) · queue flaremo-member-removalDATA_EXPORT_QUEUE→ Queues (producer) · queue flaremo-data-exportflaremo-member-removal→ Queues (consumer) · queue flaremo-member-removalflaremo-data-export→ Queues (consumer) · queue flaremo-data-exportASSETS→ Static assets
Cloudflare Workers · compatibility 2026-07-10
flaremo-site · default
Static assets: ./dist/site · 404-page
Static assets→ Static assets
Cloudflare Workers · compatibility 2026-07-10
flaremo-telegram-bot · default
Entrypoint: src/index.ts
No resource bindings declared in this scope.
Named environments are separate deployments. Bindings are shown only where declared. Configured routes are URL patterns, not verified application endpoints.
Runtime source · handlers, binding usage and workflow steps
Observed TypeScript/JavaScript declarations from Worker entrypoints and resolved relative imports. Calls and workflow steps may run conditionally; their listed order is not a proven end-to-end request flow. Router declarations may be mounted under a prefix or may not be registered. This shows code wiring, not a successful deployment or runtime test. Dynamic wiring, aliases and generated code may not resolve.
- L83 · resolveFlareMoOptions calls (conditional paths may differ): parseUserPlanLimits
- L95 · createFlareMoApp calls (conditional paths may differ): resolveFlareMoOptions, app.use, c.set, resolvePlanLimits, next, cors, includes, getTrustedOrigins, assertTrustedCookieMutation, jsonError, app.route, app.all, betterAuthRateLimitBucket, rateLimitGuard, getFlareMoAuthHandler, auth.handler, app.get, getFlareMoDb, resolveOauthIntegration, c.json
- L389 · dispatchRequestEmbeddingOutbox calls (conditional paths may differ): dispatchEmbeddingOutbox, createDb, createEmbeddingProvider, createVectorIndex, options.resolvePlanLimits, parseUserPlanLimits, options.resolveUserPlanLimits
- L408 · logBackgroundTaskFailure calls (conditional paths may differ): console.error, JSON.stringify, String
- L423 · maintenanceOptions calls (conditional paths may differ): resolvedOptions.resolvePlanLimits, parseUserPlanLimits, resolvedOptions.resolveUserPlanLimits
- L447 · createFlareMoWorker calls (conditional paths may differ): resolveFlareMoOptions, createFlareMoApp, app.fetch, request.method.toUpperCase, getFlareMoDb, catch, dispatchMemosWebhookOutbox, logBackgroundTaskFailure, dispatchRequestEmbeddingOutbox, runScheduledMaintenance, maintenanceOptions, removalJobIds.push, exportTaskIds.push, console.warn, JSON.stringify, runQueuedJobs, message.ack
Environment references: env.FLAREMO_USER_LIMITS_JSON · c.env.ASSETS · env.DB
- L27 · fetch handler exported · calls handleTelegramWebhook
- L32 · handleTelegramWebhook calls (conditional paths may differ): json, request.headers.get, Boolean, getFlaremoUrl, request.json, filter, map, env.TELEGRAM_ALLOWED_CHAT_IDS.split, value.trim, allowedChatIds.has, String, trim, fetcher, flaremoHeaders, JSON.stringify, response.json
- L123 · json calls (conditional paths may differ): Response.json
Environment references: env.TELEGRAM_WEBHOOK_SECRET · env.FLAREMO_MEMOS_PAT · env.FLAREMO_ACCESS_CLIENT_ID · env.FLAREMO_ACCESS_CLIENT_SECRET · env.FLAREMO_URL · env.TELEGRAM_ALLOWED_CHAT_IDS
- L182 · getPublicUrl calls (conditional paths may differ): isLocalDevelopmentHostname
- L218 · getTrustedOrigins calls (conditional paths may differ): getPublicUrl, configured.split, rawOrigin.trim, origins.add
- L285 · getBetterAuthSecret calls (conditional paths may differ): getRequiredBetterAuthSecret
- L289 · isLocalDevelopmentHostname calls (conditional paths may differ): hostname.endsWith
Environment references: env.FLAREMO_PUBLIC_URL · env.FLAREMO_TRUSTED_ORIGINS · env.FLAREMO_BOOTSTRAP_SECRET · env.FLAREMO_RECOVERY_SECRET · env.BETTER_AUTH_SECRET
- L51 · resolveUserLimits calls (conditional paths may differ): c.get, parseUserPlanLimits, resolve
- L66 · getFlareMoDb calls (conditional paths may differ): dbCache.get, createDb, dbCache.set
- L103 · getFlareMoAuth calls (conditional paths may differ): authCache.get, then, loadAuthFactory, createFlareMoAuth, getFlareMoDb, authCache.set
- L160 · getFlareMoAuthHandler calls (conditional paths may differ): getFlareMoDb, resolveOauthIntegrationCached, getFlareMoAuth, oauthAuthCache.get, getUserRegistrationAllowed, console.error, loadAuthFactory, createFlareMoAuth, oauthAuthCache.set
- L195 · getRequestContext calls (conditional paths may differ): getFlareMoDb, getBearerToken, assertTrustedBearerOrigin, token.startsWith, authenticateMemosAccessToken, assertActiveMember, memoFilterScanLimit, c.get, resolveUserLimits, getFlaremoUserByAuthSessionToken, getFlareMoAuth, auth.api.verifyApiKey, getFlaremoUserByAuthUserId, getBrowserRequestContext
- L288 · getOptionalRequestContext calls (conditional paths may differ): getRequestContext, c.req.raw.headers.has, getFlareMoDb, memoFilterScanLimit, c.get
- L315 · getBrowserRequestContext calls (conditional paths may differ): c.req.raw.headers.has, getFlareMoDb, getFlareMoAuth, auth.api.getSession, assertTrustedCookieMutation, getFlaremoUserByAuthUserId, assertActiveMember, memoFilterScanLimit, browserAuthUserSummary, c.get, resolveUserLimits
- L348 · assertTrustedCookieMutation calls (conditional paths may differ): isUnsafeMethod, c.req.header, includes, getTrustedOrigins
- L363 · assertRequestCredentialBoundary calls (conditional paths may differ): getBearerToken, assertTrustedBearerOrigin, c.req.raw.headers.has, assertTrustedCookieMutation
- L374 · assertTrustedBearerOrigin calls (conditional paths may differ): c.req.header, includes, getTrustedOrigins
- L384 · isUnsafeMethod calls (conditional paths may differ): includes, method.toUpperCase
- L388 · getBearerToken calls (conditional paths may differ): headers.get, split, value.trim, scheme.toLowerCase
- L398 · assertActiveMember calls (conditional paths may differ): isActiveTeamMember
Environment references: c.env.FLAREMO_USER_LIMITS_JSON · env.DB
- L17 · resolveEmbeddingConfig calls (conditional paths may differ): trim, Number.parseInt, String, Number.isFinite
- L35 · createEmbeddingProvider calls (conditional paths may differ): resolveEmbeddingConfig
- L69 · memoSearchNamespaces calls (conditional paths may differ): trim, memoUserNamespace, memoTeamNamespace
Environment references: env.FLAREMO_EMBEDDING_PROVIDER · env.FLAREMO_EMBEDDING_MODEL · env.FLAREMO_EMBEDDING_DIMENSIONS · env.FLAREMO_EMBEDDING_API_URL · env.FLAREMO_EMBEDDING_API_KEY · env.VECTORIZE_MEMOS · env.VECTORIZE_MEMORIES · env.FLAREMO_VECTORIZE_TEAM_LAYOUT · env.AI
- L56 · parseEmailPayload calls (conditional paths may differ): EMAIL_PAYLOAD.parse
- L60 · openEmailIntegration calls (conditional paths may differ): openIntegrationCredentials, integrationEncryptionSecret, console.warn
- L93 · resolveEmailIntegration calls (conditional paths may differ): envEmailConfig, readIntegrationConfig, console.warn, openEmailIntegration
- L161 · parseOauthPayload calls (conditional paths may differ): OAUTH_PAYLOAD.parse
- L165 · envOauthConfig calls (conditional paths may differ): pick
- L186 · openOauthIntegration calls (conditional paths may differ): openIntegrationCredentials, integrationEncryptionSecret, console.warn
- L208 · resolveOauthIntegration calls (conditional paths may differ): envOauthConfig, readIntegrationConfig, openOauthIntegration, console.warn
- L242 · resolveOauthIntegrationCached calls (conditional paths may differ): oauthCache.get, Date.now, resolveOauthIntegration, oauthCache.set
Environment references: env.FLAREMO_EMAIL_PROVIDER · env.FLAREMO_EMAIL_FROM · env.RESEND_API_KEY · env.FLAREMO_OAUTH_GOOGLE_CLIENT_ID · env.FLAREMO_OAUTH_GOOGLE_CLIENT_SECRET · env.FLAREMO_OAUTH_GITHUB_CLIENT_ID · env.FLAREMO_OAUTH_GITHUB_CLIENT_SECRET
- L17 · clientIpFromRequest calls (conditional paths may differ): request.headers.get
- L31 · checkRateLimit calls (conditional paths may differ): clientIpFromRequest, limiter.limit, console.error, JSON.stringify, String
- L59 · rateLimitGuard calls (conditional paths may differ): checkRateLimit, c.json
- L88 · betterAuthRateLimitBucket calls (conditional paths may differ): RATE_LIMITED_AUTH_PATHS.some, pathname.includes
Environment references: c.env.RATE_LIMITER
- L64 · parseTrashRetentionDays calls (conditional paths may differ): Number.parseInt, Number.isFinite, Math.min
- L83 · runMemberRemovalJobs calls (conditional paths may differ): claimMemberRemovalJob, updateMemberRemovalJob, beginFlaremoMemberRemoval, cleanupFlaremoArtifacts, finalizeFlaremoMemberRemoval, toISOString, catch, failMemberRemovalJob
- L117 · runExportTasks calls (conditional paths may differ): runDataExportTask, catch, failDataTask
- L143 · runQueuedJobs calls (conditional paths may differ): createDb, getQueuedMemberRemovalJobsByIds, runMemberRemovalJobs, runExportTasks
- L156 · runScheduledMaintenance calls (conditional paths may differ): createDb, requeueStaleMemberRemovalJobs, getQueuedMemberRemovalJobsByIds, listQueuedMemberRemovalJobs, runMemberRemovalJobs, Boolean, map, listQueuedDataExportTasks, runExportTasks, dispatchMemosWebhookOutbox, runMemoryLedgerMaintenance, runMemoryDreaming, console.log, JSON.stringify, runMemoryConflictPatrol, console.error, String, pruneMemosSseEvents, dispatchEmbeddingOutbox, createEmbeddingProvider
Environment references: env.DB · env.FLAREMO_USER_LIMITS_JSON · env.ATTACHMENTS · env.FLAREMO_TRASH_RETENTION_DAYS · env.FLAREMO_VAPID_PUBLIC_KEY · env.FLAREMO_VAPID_PRIVATE_KEY
- L26 · onceSubApp calls (conditional paths may differ): loaded.get, catch, load, loaded.delete, loaded.set
- L43 · forward calls (conditional paths may differ): url.pathname.slice, app.fetch
- L72 · mountLazyRoute calls (conditional paths may differ): app.all, forward, onceSubApp
- L89 · mountLazySsrPages calls (conditional paths may differ): Promise.all, registerSharePage, registerArticlePage, loadPages, app.all, pages.then, p.fetch
- L49 · accountApi.get("/personal-access-tokens")
- L64 · accountApi.post("/personal-access-tokens")
- L99 · accountApi.post("/personal-access-tokens/:id/revoke")
- L126 · accountApi.delete("/personal-access-tokens/:id")
- L142 · accountApi.post("/email")
- L220 · accountApi.delete("/")
- L284 · toPersonalAccessTokenDto calls (conditional paths may differ): toIsoDate, token.createdAt.toISOString, token.updatedAt.toISOString
- L316 · toIsoDate calls (conditional paths may differ): value.toISOString
Environment references: c.env.MEMBER_REMOVAL_QUEUE
- L56 · authApi.get("/bootstrap/status")
- L78 · authApi.get("/register/status")
- L94 · authApi.post("/register")
- L192 · authApi.get("/verify-email")
- L215 · authApi.post("/resend-verification")
- L266 · authApi.post("/forgot-password")
- L317 · authApi.get("/verify-email-change")
- L357 · authApi.post("/bootstrap")
- L456 · authApi.post("/recover")
- L540 · authApi.post("/recover-bootstrap")
- L572 · secretsMatch calls (conditional paths may differ): Promise.all, crypto.subtle.digest, encoder.encode
Environment references: c.env.ATTACHMENTS
- L54 · memoryApi.get("/")
- L83 · memoryApi.get("/review")
- L92 · memoryApi.get("/compile")
- L109 · memoryApi.get("/lens")
- L129 · memoryApi.post("/")
- L145 · memoryApi.get("/:id")
- L156 · memoryApi.patch("/:id")
- L172 · memoryApi.delete("/:id")
- L187 · memoryApi.post("/:id/confirm")
- L203 · memoryApi.post("/:id/lock")
- L219 · memoryApi.post("/:id/pin")
- L235 · memoryApi.post("/:id/unlock")
- L251 · memoryApi.post("/:id/unpin")
- L267 · memoryApi.post("/:id/archive")
- L283 · memoryApi.post("/:id/restore")
- L299 · memoryApi.post("/proposals/:id/resolve")
- L319 · memoryApi.post("/:id/split-key")
- L345 · memoryApi.post("/:id/promote")
- L361 · memoryApi.get("/:id/revisions")
- L376 · memoryApi.get("/:id/relations")
- L391 · memoryApi.get("/:id/evidence")
- L406 · memoryApi.get("/:id/lineage")
- L50 · parseMemoryId calls (conditional paths may differ): value.startsWith
- L30 · memosConnectApi.post("/:service/:method")
- L18 · memosFileApi.get("/attachments/:attachment/:filename")
- L49 · serveAttachment calls (conditional paths may differ): attachmentObjectResponse, c.req.query, c.json
- L70 · normalizeAttachmentName calls (conditional paths may differ): value.startsWith
Environment references: c.env.ATTACHMENTS
- L24 · memosSseApi.get("/api/v1/sse")
Build and deployment pipeline · 3 GitHub Actions workflows
Repository CI declarations, separate from runtime request processing. Job dependencies and conditions are shown as written; long commands are shortened with an ellipsis; a workflow file does not prove a recent successful run.
Triggers: push, pull_request
checks (format / lint / typecheck / unit) · no job dependencies declared
- actions/checkout@v7
actions/checkout@v7 - pnpm/action-setup@v6
pnpm/action-setup@v6 - actions/setup-node@v7
actions/setup-node@v7 - Install dependencies
pnpm install --frozen-lockfile - Format check
pnpm format:check - Lint & typecheck
pnpm check - Unit tests
pnpm test
Triggers: workflow_dispatch
deploy · no job dependencies declared
Condition: github.repository != 'realchendahuang/FlareMo'
- Check Cloudflare credentials
set -euo pipefail if [[ -z "${CLOUDFLARE_API_TOKEN}" ]]; then echo "Missing repository secret CLOUDFLARE_API_TOKEN." >&2 exit 1 fi if [[ -z "${CLOUDFLARE_ACCOUNT_ID}" ]]; then echo "Missing repository secret CLOUDFLARE_ACCOUNT_ID." >&2 exit 1 fi - actions/checkout@v7
actions/checkout@v7 - pnpm/action-setup@v6
pnpm/action-setup@v6 - actions/setup-node@v7
actions/setup-node@v7 - Install dependencies
pnpm install --frozen-lockfile - Write wrangler.jsonc
node ./scripts/write-wrangler-config.mjs - Provision / verify Cloudflare resources
set -euo pipefail if [[ "${{ inputs.provision }}" == "true" && "${{ inputs.dry_run }}" != "true" ]]; then pnpm provision:remote else pnpm provision:remote -- --check fi - Deploy
set -euo pipefail if [[ "${{ inputs.dry_run }}" == "true" ]]; then pnpm deploy:dry-run else pnpm deploy fi - Sync Worker secrets
pnpm secrets:syncCondition: ${{ inputs.dry_run != true }} - Smoke check deployed site
pnpm smoke:checkCondition: ${{ inputs.dry_run != true }}
Triggers: schedule, workflow_dispatch
prepare-update · no job dependencies declared
Condition: github.repository != 'realchendahuang/FlareMo'
- Check out deployment repository
actions/checkout@v7 - Prepare update pull request
set -euo pipefail target_version="${REQUESTED_VERSION}" if [[ -z "${target_version}" ]]; then target_version="$(gh api "repos/${UPSTREAM_REPOSITORY}/releases/latest" --jq .tag_name)" fi if [[ ! "${target_version}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "Invalid FlareMo release tag: ${target_version}" >&2 exit 1 fi current_version="$(node -p "JSON.parse(require('node:fs').readFileSync('package.json', 'utf8')).version")" if [[ ! "${current_version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "…
build: pnpm -r buildbuild:site: pnpm plugins:build && pnpm --filter @flaremo/site buildrelease: node ./scripts/release.mjsdeploy:preflight: node ./scripts/deploy-preflight.mjsmigrate:local: wrangler d1 migrations apply DB --local --config ./wrangler.jsoncmigrate:remote: wrangler d1 migrations apply DB --remote --config ./wrangler.jsoncdeploy:dry-run: pnpm --filter @flaremo/web build && wrangler deploy --config ./wrangler.jsonc --dry-rundeploy:dry-run:site: pnpm build:site && wrangler deploy --config ./apps/site/wrangler.jsonc --dry-rundeploy: pnpm deploy:preflight && pnpm --filter @flaremo/web build && pnpm migrate:remote && pnpm --filter @flaremo/worker deploydeploy:site: pnpm build:site && wrangler deploy --config ./apps/site/wrangler.jsonc
build: node ./scripts/build.mjsdeploy: pnpm build && wrangler deploy --config ./wrangler.jsoncdeploy:dry-run: pnpm build && wrangler deploy --config ./wrangler.jsonc --dry-run --outdir=dist
build: tsc -b
build: tsc -b && tsc --noEmit -p tsconfig.team-projects.json && vite build
deploy: wrangler deploy --config ../../wrangler.jsoncbuild: tsc -b
build: tsc -b
build: tsc -b
build: tsc -b
build: tsc -b
build: tsc -b
Repository README
View original on GitHub ↗Full upstream document by @realchendahuang · README.md · snapshot 5f43c8e
FlareMo 🔥
Zero Servers · Zero Upkeep · 24/7 Global Edge Uptime · Absolute Data Ownership
For individuals: a quiet, focused thought capture space and second brain. For teams: a shared knowledge base with fine-grained roles.
English • 简体中文 • 日本語 • Français • Español • 한국어 • Русский • العربية
| ☀️ Desktop · Light Theme | 🌙 Desktop · Dark Theme | 📱 Mobile · Responsive |
|---|---|---|
![]() |
![]() |
![]() |
Actual live screenshots: seamless light/dark mode switching and full-featured mobile responsiveness. Every feature shown is wired to working backend capabilities.
💡 Why FlareMo?
Tools like Flomo and Memos proved the immense value of low-friction memo capture and a distraction-free timeline. However, self-hosting a traditional note-taking setup typically means paying for a VPS, configuring Docker and PostgreSQL, scripting automated backups, and dreading disk or hardware failures.
FlareMo answers a simpler question: Can you get a 24/7 online, resilient, globally accelerated knowledge base with zero server maintenance, using just a free Cloudflare account?
- Truly Serverless: Both code and static assets run on Cloudflare Workers edge nodes near you with millisecond latency.
- Enterprise-grade durability out of the box: Cloudflare D1 handles notes and metadata; Cloudflare R2 stores media attachments with multi-region replication.
- AI-Native Second Brain: Agent Memory hub ships a CLI and a cross-agent skill so AI agents (Claude, Cursor, Codex, ChatGPT, ZCode) can read and update your long-term preferences and memory scopes; MCP endpoints are also available.
- Quiet for one, powerful for many: Default is an encrypted, private single-user sanctuary. Enable team mode, and it instantly transforms into a collaborative workspace with roles and three-tier visibility.
- Minimal, not simplistic: The interface stays quiet and every control earns its place — nothing decorative shouting for attention, nothing useful missing.
✨ Key Features
1. Instant Capture & Inspiring Review
- Capture in milliseconds: Card-style timeline, tags, Markdown/GFM, and previews for image and audio attachments.
- Lightning-fast search: SQLite FTS5 full-text indexing with query operators (
has:attachment,is:pinned,before:YYYY-MM-DD,after:YYYY-MM-DD,in:timeline|archive|trash). - Semantic "Find" (Vector Search): Workers AI embeddings paired with Vectorize derived vector indexes for contextual recall; re-verifies permissions against D1 and seamlessly falls back to FTS5.
- Thought activation: Built-in Daily Review (on this day), Random Walk (wandering through tag and backlink graphs with postcard summaries), and related note recommendations.
- Revision history: Full version diffs and one-click historical restore.
2. AI Long-term Memory (CLI + Skills)
- Agent Memory: Ships
flaremoCLI and theflaremo-memoryskill — agents record and update cross-session long-term memory (preferences, project decisions, constraints, lessons) through a shared REST base. - Human in the loop: Review, verify, lock, or correct AI-recorded memories at
/memory. - Open ecosystem: CLI + Skills are the recommended path;
/memory/mcp(Streamable HTTP MCP) and/mcpendpoints are available for existing MCP clients.
3. Projects & Tasks
- Group work under projects: Organize notes and to-dos into projects, with a kanban board (drag between status columns), priorities, manual sort order, and due dates.
- Personal by design, reversible deletion: Tasks belong to a single owner; deleting moves them to a recycle bin until restored or automatically purged.
4. Task Management & Reminders
- Tasks live in Projects: The
/projectsboard (drag between status columns), priorities, manual sort order, and due dates make project pages the single home for scheduling work. - Time horizons at a glance: The explorer home view pairs a mini month calendar with overdue/today reminders, so what's due never hides behind the board.
- Overdue reminders: Overdue tasks raise in-app notifications, with optional browser Web Push.
5. Team Collaboration & 3-Tier Visibility
- Role governance:
owner,admin, andmemberroles. Admins invite members via one-time activation links (members choose their own passwords; admins never handle plaintext credentials). - 3-tier visibility:
- 🔒 Private: Only author can view.
- 👥 Team: Shared read-only with active team members.
- 🌐 Public: Anonymous read-only via time-limited share links.
- Safe offboarding: Removing a member triggers reliable background cleanup that purges private data while preserving team and public notes.
- Reader seats: Grant a time-boxed read-only seat — guest readers, course cohorts, client delivery. Seats lapse automatically at their expiry (fail-closed at credential resolution, no cron needed). Manage them from the members page, or provision by email through
PUT /api/app/admin/team/readerwith a Personal Access Token (seedocs/team-mode.md).
6. Offline First & PWA Experience
- Installable PWA: Install to macOS, Windows, iOS, or Android home screen with native feel.
- Reliable offline sync: Drafts save locally instantly. Offline submissions and uploads queue up and replay automatically when connectivity is restored.
- Live voice capture: Access
/capturefor real-time streaming speech-to-text (ASR) transcription.
7. Secure Better Auth Application Security
- Better Auth powered: HttpOnly,
SameSite=Laxbrowser cookie sessions; revocablememos_pat_Personal Access Tokens for scripts, CLI, and MCP. - Strict Origin protection: State-changing requests enforce exact origin whitelisting. Cloudflare Access remains available as an optional outer defensive perimeter.
8. Memos Compatibility & Seamless Migration
- Memos
/api/v1compatibility: Provides core Memos API endpoints (camelCase default, legacy snake_case via header) and OpenAPI schema. - Third-party apps ready: Works directly with mobile clients like Moe Memos.
- Bi-directional import & export: One-click import from Memos / flomo with conflict strategies and full raw export bundles.
9. Plugin System: Cards as Plugins
- Five built-in cards: Plain, Daily, Ticket, Postcard, plus a canvas-drawn Postmark demo.
- Store and curation: browse directories, one-click install (SHA-256 verified), enable/disable, reorder, set the default, hide — all in account settings. The official directory lives at flaremo.app/plugins.
- Upload your own: admins can install a local package — it exists only on that instance and is never sent anywhere.
- Authoring tools:
pnpm plugin:newscaffolds,pnpm plugin:checkvalidates with the exact rules instances enforce on install,pnpm plugins:buildpackages. Document cards are pure JSON layouts; sandbox cards run your own HTML/CSS/JS. See the plugin guide. - Safe by default: cards run in an opaque-origin sandbox with no network access; community and brand packs stay off until an admin enables them.
📊 How Generous Is Cloudflare's Free Tier?
Many assume "free" means "severely limited". For text-heavy personal knowledge bases, Cloudflare's free quota is virtually inexhaustible:
| Resource | Free Tier Quota | Equivalent Capacity | Practical Lifespan |
|---|---|---|---|
| Cloudflare D1 | 5 GB database | ~2.5 Million text memos | Writing 100 memos daily would take 68 years to fill |
| Cloudflare R2 | 10 GB storage | ~5,000–10,000 photos / 80 hours of voice | $0 egress fees; public sharing won't trigger bandwidth bills |
| Cloudflare Workers | Generous free request limits | 300+ global edge locations | Millisecond latency worldwide without cold boots |
🥊 Comparison: Cloudflare Native vs Home NAS vs Traditional VPS
| Dimension | Cloudflare Native (FlareMo) | Home NAS / Mini PC | Traditional VPS |
|---|---|---|---|
| Data Durability | Enterprise multi-region replication, zero hardware failure risk | Single drive failure or power outage can cause total data loss | Dependent on manual snapshot & backup routines |
| Maintenance | Zero: No OS patching, no Docker compose, no DB maintenance | OS updates, Docker upkeep, SMART disk alerts, router configs | Kernel upgrades, security patches, watchdog daemons |
| Access Latency | Global edge CDN, sub-100ms response anywhere | Requires DDNS / frp / Tailscale tunnels, constrained by home uplink | Dependent on single cloud region; high cross-border latency |
| SSL & Domains | Automated HTTPS & custom domain bindings | Manual certificate issuance, reverse proxy configuration | Nginx / Caddy config & Let's Encrypt renewal maintenance |
| Financial Cost | $0 / month on free tier | High upfront hardware cost + ongoing electricity | Ongoing monthly / annual server & bandwidth bills |
🚀 5-Minute Quick Deployment
Method 1: One-click Deploy to Cloudflare
Clones the repository into your GitHub account and provisions D1, R2, Queues, and Vectorize automatically. After the initial deploy, set FLAREMO_PUBLIC_URL and secrets (see docs/en/deploy.md). If the first attempt reports "Github API Limit Exceeded", wait a few minutes and retry.
Method 2: GitHub Action (self-hosted fork)
On your fork, run Deploy to Cloudflare from Actions to provision resources, publish the Worker, and sync auth secrets. Pushes do not publish. See docs/en/github-action-deploy.md.
Method 3: Deploy with an AI Agent (Recommended)
Give the repository to an agent capable of executing terminal commands (e.g. Claude Code, Cursor Agent, Codex) along with docs/en/agent-deploy.md:
"Please deploy FlareMo to my Cloudflare account following docs/en/agent-deploy.md."
Method 4: Manual 3-Step Deployment
1. Create Cloudflare Resources
pnpm exec wrangler whoami
pnpm exec wrangler d1 create flaremo
pnpm exec wrangler r2 bucket create flaremo-attachments
Or run pnpm provision:remote instead: it creates the missing D1 / R2 / Queue / Vectorize resources and writes the D1 database_id into wrangler.jsonc for you. It is idempotent — existing resources are skipped.
2. Configure Settings & Secrets
cp wrangler.jsonc.example wrangler.jsonc
Fill in the generated database_id and set FLAREMO_PUBLIC_URL to your production domain. Then set secrets:
pnpm exec wrangler secret put BETTER_AUTH_SECRET --config ./wrangler.jsonc
pnpm exec wrangler secret put FLAREMO_BOOTSTRAP_SECRET --config ./wrangler.jsonc
3. Deploy
pnpm deploy:dry-run
pnpm deploy
(The full pnpm verify gate runs only when the maintainer explicitly asks for it.)
Visit your production domain at /setup and enter the FLAREMO_BOOTSTRAP_SECRET to initialize your Owner account.
Detailed guides: Deployment Guide · GitHub Action deploy · Update Guide.
🧱 Architecture & Tech Stack
flowchart LR
Browser["FlareMo Web UI (React 19 / PWA)"] --> Worker["Cloudflare Worker"]
Clients["Memos Clients / Scripts / MCP"] --> Worker
Worker --> Auth["Better Auth (Session / PAT)"]
Worker --> D1["Cloudflare D1 (Memos / Relations / Settings)"]
Access["Cloudflare Access (Optional Outer Perimeter)"] -.-> Worker
Worker --> R2["Cloudflare R2 (Attachments & Exports)"]
Worker --> Assets["Workers Static Assets"]
- Runtime: Cloudflare Workers
- Frontend: React 19, Vite, TanStack Router, Tailwind CSS 4, Radix UI
- Database: Cloudflare D1, Drizzle ORM
- Storage: Cloudflare R2
- Auth: Better Auth (HttpOnly cookie session + revocable
memos_pat_) - AI & Search: Workers AI, Vectorize, SQLite FTS5
- Plugins: slot-based extension platform (standard, guide); packages live in R2, sandboxed cards run without network access
🌟 Star History
📄 License
Open-sourced under the GNU AGPL-3.0 license. Copyright (c) 2026 realchendahuang.
Frequently asked about FlareMo
What is FlareMo?+
FlareMo is a self-hosted Memos alternative built on the Cloudflare developer platform. Keep notes, attachments and searchable agent memories on your Cloudflare account.
What does FlareMo replace?+
FlareMo is listed as an alternative to Memos. Compare the features and tradeoffs before migrating.
What Cloudflare primitives does FlareMo use?+
FlareMo is built on D1, Queues, R2, Vectorize, Workers, Workers AI.
How much does FlareMo cost to run?+
The documented FlareMo deployment can use Cloudflare Free allowances for a small workload under the request, CPU and service-specific quotas below. This is conditional eligibility, not a measured zero-cost deployment; optional features, domains and external providers can add costs. Workers Free allows 100,000 requests per day shared across the account and 10 ms CPU per invocation; measure CPU-heavy authentication, parsing and rendering before assuming it fits. Keep aggregate D1 use below 5 million rows read/day, 100,000 rows written/day and 5 GB total storage; a request can touch many rows. Use R2 Standard storage, at most 10 GB-month, 1 million Class A operations and 10 million Class B operations/month; provision an eligible billing-enabled R2 account. Keep total queue operations within 10,000/day, counting writes, reads, deletes, retries and each 64 KB chunk; Free retention is 24 hours. Only ordinary Free-eligible Workers AI models are covered, within 10,000 neurons/day; optional external providers and paid-only models are excluded. Keep both indexes combined within 5 million stored dimensions and 30 million queried dimensions/month; dimensions, not note counts, determine Vectorize usage. Use a small personal or team workload; domain registration and optional third-party providers are separate costs. Provision your own IDs, secrets and migrations. Check current Cloudflare pricing before deploying.
Is FlareMo open source?+
The upstream repository declares the AGPL-3.0-only license. Read its terms at https://raw.githubusercontent.com/realchendahuang/FlareMo/5f43c8e067a309c7f4594c214f68c1b5903494ee/LICENSE. Source code and contributor credit are available at https://github.com/realchendahuang/FlareMo.





Discussion · 0
sign in to comment →