
Talorys
A personal AI assistant with memory, tasks and reminders in your own Cloudflare account
Talorys is a self-hosted ChatGPT alternative built on Cloudflare (Durable Objects, Pages, Workers, Workers AI). Free tier eligible within limits. Inspect the source and license in the linked repository.
Source & license
Upstream license: MIT
License TL;DR
You can use it, change it, self-host it and sell it. Keep the original copyright and license notice with copies of the code. You don’t have to publish your changes. The authors don’t promise it will work.
Explain MIT 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 README states it is designed to fit the Workers Free plan and does not enable paid services. Chat stops for the day when the Workers AI daily Neuron allocation is exhausted; a paid account is billed for usage above included amounts.
Hosting requirements
- Workers AI is metered in Neurons; the free daily allocation limits how much chat a free account gets.
- The installer generates the production Worker and Pages names; the checked-in agent config is the development template it is derived from.
- Semantic recall is optional and off by default.
- Source and configuration review establishes a deployment path and conditional costs; this candidate was not executed or load-tested.
- Official Cloudflare pricing pages saved 31 Oct 2026 (Markdown editions of developers.cloudflare.com pricing documentation).
Review findings & limitations
- Workers AI is metered in Neurons; the free daily allocation limits how much chat a free account gets.
- The installer generates the production Worker and Pages names; the checked-in agent config is the development template it is derived from.
- Semantic recall is optional and off by default.
- Competitor links describe the stated workflow overlap, not feature parity or a verified price saving.
- Source review at the pinned commit; deployment, workload CPU and application security have not been independently tested.
Sources checked 31/10/2026
Repository snapshot: 25781ec. Hosting eligibility reflects the deployment documentation and listed assumptions.
- chatgpt ↗
Talorys is a free, open-source personal AI assistant that runs entirely inside **your own Cloudflare account**. It chats with you, remembers what matters, manages tasks, notes and projects, and runs reminders and routines on a schedule — without any server, database or account operated by the Talorys developers.
- workers ↗
rates the production equivalent with unique names. { "$schema": "../../node_modules/wrangler/config-schema.json", "name": "talorys-dev-agent", "main": "src/index.ts", "compatibility_date": "2026-10-01", "compatibility_flags": ["nodejs_compat"], // Private backend: reachable only through the Pages service binding. "workers_dev": false, "preview_urls": false, "ai": { "binding": "AI" }, "durable_objects": { "bindings": [{ "name": "TalorysAgent", "class_name": "TalorysAgent" }]
- durable-objects ↗
ges service binding. "workers_dev": false, "preview_urls": false, "ai": { "binding": "AI" }, "durable_objects": { "bindings": [{ "name": "TalorysAgent", "class_name": "TalorysAgent" }] }, "migrations": [{ "tag": "v1", "new_sqlite_classes": ["TalorysAgent"] }], "observability": { "enabled": true } }
- workers-ai ↗
pat"], // Private backend: reachable only through the Pages service binding. "workers_dev": false, "preview_urls": false, "ai": { "binding": "AI" }, "durable_objects": { "bindings": [{ "name": "TalorysAgent", "class_name": "TalorysAgent" }] }, "migrations": [{ "tag": "v1", "new_sqlite_classes": ["TalorysAgent"] }], "observability": { "enabled": true } }
- pages ↗
nerates the production equivalent with unique names. { "$schema": "../../node_modules/wrangler/config-schema.json", "name": "talorys-dev-web", "pages_build_output_dir": "./dist", "compatibility_date": "2026-10-01", "services": [{ "binding": "AGENT", "service": "talorys-dev-agent" }] }
- free-tier-eligible ↗
Talorys is designed to fit the **Cloudflare Workers Free plan** and never enables paid features on its own.
- free-tier-eligible ↗
| **Free** | 100,000 per day | No charge for duration | 10 milliseconds of CPU time per invocation |
- free-tier-eligible ↗
| SQL Stored data <sup>5</sup> | 5 GB (total) | 5 GB-month, + $0.20/ GB-month |
- free-tier-eligible ↗
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.
- MIT ↗
MIT License Copyright (c) 2026 The Talorys contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
- architecture ↗
Talorys is a free, open-source personal AI assistant that runs entirely inside **your own Cloudflare account**.
- architecture ↗
Talorys does **not** provision R2, D1, KV, Vectorize, AI Search, Workflows or any paid service.
- architecture ↗
rates the production equivalent with unique names. { "$schema": "../../node_modules/wrangler/config-schema.json", "name": "talorys-dev-agent", "main": "src/index.ts", "compatibility_date": "2026-10-01", "compatibility_flags": ["nodejs_compat"], // Private backend: reachable only through the Pages service binding. "workers_dev": false, "preview_urls": false, "ai": { "binding": "AI" }, "durable_objects": { "bindings": [{ "name": "TalorysAgent", "class_name": "TalorysAgent" }]
- architecture ↗
ges service binding. "workers_dev": false, "preview_urls": false, "ai": { "binding": "AI" }, "durable_objects": { "bindings": [{ "name": "TalorysAgent", "class_name": "TalorysAgent" }] }, "migrations": [{ "tag": "v1", "new_sqlite_classes": ["TalorysAgent"] }], "observability": { "enabled": true } }
- architecture ↗
pat"], // Private backend: reachable only through the Pages service binding. "workers_dev": false, "preview_urls": false, "ai": { "binding": "AI" }, "durable_objects": { "bindings": [{ "name": "TalorysAgent", "class_name": "TalorysAgent" }] }, "migrations": [{ "tag": "v1", "new_sqlite_classes": ["TalorysAgent"] }], "observability": { "enabled": true } }
- architecture ↗
nerates the production equivalent with unique names. { "$schema": "../../node_modules/wrangler/config-schema.json", "name": "talorys-dev-web", "pages_build_output_dir": "./dist", "compatibility_date": "2026-10-01", "services": [{ "binding": "AGENT", "service": "talorys-dev-agent" }] }
Upstream screenshot · rociiu/talorys 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.
Personal assistant chat with saved memory, tasks, notes and scheduled reminders for one person; frontier hosted models, plugins, image generation and shared workspaces are not included
See supporting source ↗How it works
The shape of Talorys 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 25781ec26283. This configuration evidence comes from reading source files. Execution checks, when available, appear in the project's runtime review.
Deployment configuration · 3 files
Cloudflare Workers · compatibility 2026-10-01
talorys-dev-agent · default
Entrypoint: src/index.ts
TalorysAgent→ Durable Objects · class TalorysAgentAI→ Workers AI
Cloudflare Workers · compatibility 2026-10-01
talorys · default
Entrypoint: src/index.ts
Static assets: ../web/dist · single-page-application · Worker first: ["/api/*"]
TalorysAgent→ Durable Objects · class TalorysAgentAI→ Workers AIStatic assets→ Static assets
Cloudflare Pages · compatibility 2026-10-01
talorys-dev-web · default
AGENT→ Worker service · service talorys-dev-agent
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.
- L17 · fetch handler exported · references TalorysAgent · calls includes, url.pathname.split, url.pathname.startsWith, Response.json, request.headers.get, getAgentByName, agent.fetch
Environment references: env.TalorysAgent
- L12 · Cloudflare Pages ALL request handler exported · calls Response.json, env.AGENT.fetch, console.error
Environment references: env.AGENT
- L177 · skipForBudget calls (conditional paths may differ): s.notifications.create
Environment references: env.TALORYS_ENV · env.TALORYS_MOCK_AI · env.AI
- L44 · createServices calls (conditional paths may differ): push.notify
Environment references: env.TALORYS_DEFAULT_TIMEZONE · env.TALORYS_AGENT_NAME · env.OWNER_PASSWORD_HASH · env.OWNER_PASSWORD · env.SESSION_SECRET
- L18 · titleFromMessage calls (conditional paths may differ): trim, message.replace, clip, cut.lastIndexOf, cut.slice
- L26 · toolLabel calls (conditional paths may differ): tool.input.safeParse, name.replace, tool.label
- L37 · toProviderTools calls (conditional paths may differ): map, executor.available, executor.execute
- L75 · withApprovalOutcomes calls (conditional paths may differ): m.tools.flatMap, approvalNote, planOutcome, trim, notes.join
- L37 · app.use("*")
- L39 · app.use("*")
- L40 · app.use("*")
- L41 · app.use("*")
- L44 · app.route("/")
- L45 · app.route("/")
- L46 · app.route("/auth")
- L47 · app.route("/")
- L48 · app.route("/")
- L49 · app.route("/")
- L50 · app.route("/")
- L51 · app.route("/")
- L52 · app.route("/")
- L31 · createApi calls (conditional paths may differ): basePath, app.onError, errorResponse, app.notFound, app.use, app.route
- L73 · planTools calls (conditional paths may differ): text.match, map, filter, split, t.trim, trim, t.replace, t.endsWith, slice, test, tasks.join, Number, JSON.parse, JSON.stringify
- L73 · estimateTokens calls (conditional paths may differ): Math.ceil
- L78 · tokenCount calls (conditional paths may differ): Number.isSafeInteger
- L89 · withUsage calls (conditional paths may differ): failedCallUsage.set
- L95 · usageOfFailure calls (conditional paths may differ): failedCallUsage.get
- L99 · modelUsage calls (conditional paths may differ): tokens, Math.min
- L103 · estimatedUsage calls (conditional paths may differ): modelUsage, tokens, estimateTokens
- L111 · classifyAiError calls (conditional paths may differ): errorMessage, message.toLowerCase, lower.includes, console.error
- L134 · errorMessage calls (conditional paths may differ): errorMessage, String
- L10 · parseLocalDateTime calls (conditional paths may differ): value.trim, trimmed.match, isValidCalendarDate, Number, zonedTimeToUtc, Number.isNaN, date.getTime, date.toISOString, Date.parse, toISOString
- L31 · toLocalMinute calls (conditional paths may differ): getZonedParts, padStart, String, pad
- L38 · localDayBounds calls (conditional paths may differ): getZonedParts, zonedTimeToUtc, Date.UTC, next.getUTCFullYear, next.getUTCMonth, next.getUTCDate, start.toISOString, end.toISOString
- L46 · formatLocal calls (conditional paths may differ): format
- L19 · invalidateSemanticCache calls (conditional paths may differ): neverEnabled.delete
- L28 · cachedSemanticConfig calls (conditional paths may differ): neverEnabled.get, readSemanticConfig, neverEnabled.set
- L43 · seedSemanticDefault calls (conditional paths may differ): cachedSemanticConfig, writeSemanticConfig, invalidateSemanticCache, services.activity.record
- L57 · semanticMemorySelector calls (conditional paths may differ): selectSemanticMemories, console.warn, String, selectRelevantMemories
- L75 · catchUpItems calls (conditional paths may differ): itemPassRunning, runItemPass, console.warn, String
- L90 · semanticItemSelector calls (conditional paths may differ): catchUpItems, peekQueryVector, queryVector, itemSection, selectSemanticItems, console.warn, String
- L108 · semanticExtensions calls (conditional paths may differ): seedSemanticDefault, cachedSemanticConfig, ensureSemanticSchema, createSemanticContext, semanticMemorySelector, semanticItemSelector, semanticTools, console.warn
- L56 · errorResponse calls (conditional paths may differ): console.error, JSON.stringify, appError.toBody
- L29 · semanticNeighbors calls (conditional paths may differ): cachedSemanticConfig, ensureSemanticSchema, createSemanticContext, embeddingText, contentHash, vectorMetaFor, get, cachedVectors, UsageService.day, reserveEmbedding, estimateTokens, embedder.embed, upsertVectors, now, console.warn, opts.exclude.has, scored.push, dot, scored.sort, services.memories.get
- L44 · tokens calls (conditional paths may differ): match, text.toLowerCase, STOP.has, GENERIC.has, out.add, raw.endsWith, raw.slice
- L104 · slotSearchTerms calls (conditional paths may differ): map, SLOTS.filter, s.about.test
- L116 · classifyRelationship calls (conditional paths may differ): toLowerCase, newContent.trim, old.content.trim, tokens, filter, newTokens.has, EXPLICIT_CHANGE.test, join, shared.slice, SLOTS.find, s.about.test, slot.change.test
- L204 · clefUsage calls (conditional paths may differ): tokenCount, modelUsage, tokenUsage, estimateTokens, JSON.stringify
- L210 · clefRequest calls (conditional paths may differ): newer.slice, older.map, o.content.slice, Object.fromEntries
- L238 · parseClefAnswers calls (conditional paths may differ): clefResponseSchema.safeParse, olderIds.forEach, clefAnswerSchema.safeParse, Math.round, out.push
- L258 · aiCheckSupersession calls (conditional paths may differ): parseClefAnswers, run, clefRequest, older.map
- L33 · normalize calls (conditional paths may differ): JSON.stringify
- L44 · likePattern calls (conditional paths may differ): q.replace
- L49 · ftsTerms calls (conditional paths may differ): slice, filter, match, q.toLowerCase, STOPWORDS.has
- L57 · ftsQuery calls (conditional paths may differ): ftsTerms, join, terms.map
- L153 · stripUndefined calls (conditional paths may differ): Object.fromEntries, filter, Object.entries
- L17 · toAutomation calls (conditional paths may differ): JSON.parse, describeSchedule
- L27 · present calls (conditional paths may differ): now, row.tool.startsWith
- L90 · stripUndefined calls (conditional paths may differ): Object.fromEntries, filter, Object.entries
- L29 · toPushMessage calls (conditional paths may differ): trim, replace, text.replace, clip, shorten, plain
Environment references: env.VAPID_PUBLIC_KEY · env.VAPID_PRIVATE_KEY · env.VAPID_SUBJECT
- L35 · utcDay calls (conditional paths may differ): slice, date.toISOString
- L36 · present calls (conditional paths may differ): now, JSON.parse
Environment references: env.MCP_CREDENTIAL_KEY · env.TALORYS_MCP · env.TALORYS_ENV · env.TALORYS_MCP_MAX_TOOLS
- L5 · clip calls (conditional paths may differ): segmenter.segment, text.slice, text.charCodeAt, Math.max
- L54 · buildSystemPrompt calls (conditional paths may differ): format, BASE_SYSTEM_PROMPT.replace, ctx.memories.filter, sections.push, join, current.map, uncertain.map, past.map, sections.join
- L14 · selectRelevantMemories calls (conditional paths may differ): hasHistoricalIntent, repo.search, selected.set, selected.values, repo.predecessors, repo.list
- L34 · memoryTokenBudget calls (conditional paths may differ): Math.min, Math.floor
- L48 · buildContextWindow calls (conditional paths may differ): history.filter, m.content.trim, estimateTokens, kept.unshift, kept.shift, eligible.slice, kept.map, m.content.slice
- L74 · summarizationPrompt calls (conditional paths may differ): join, overflow.map, m.content.slice
- L23 · allowedOrigins calls (conditional paths may differ): isDevelopment, split, o.trim, origins.add
- L58 · readSessionToken calls (conditional paths may differ): c.req.header, sessionCookieName, header.split, split, part.trim, decodeURIComponent, rest.join
- L76 · sessionCookie calls (conditional paths may differ): sessionCookieName, encodeURIComponent, expires.toUTCString, attrs.push, attrs.join
- L105 · body calls (conditional paths may differ): Number, c.req.header, readLimited, JSON.parse, schema.safeParse, ValidationError.fromZod
- L125 · readLimited calls (conditional paths may differ): request.body.getReader, reader.read, reader.cancel, chunks.push, bytes.set, decode
- L149 · query calls (conditional paths may differ): schema.safeParse, c.req.query, ValidationError.fromZod
- L155 · clientId calls (conditional paths may differ): clientKey, c.req.header
- L160 · ipv4Octets calls (conditional paths may differ): text.split, test, Number, octets.push
- L174 · expandIPv6 calls (conditional paths may differ): addr.split, half.split, part.includes, ipv4Octets, out.push, test, Number.parseInt, pieces, fill, Array
- L208 · clientKey calls (conditional paths may differ): ip.includes, split, ip.toLowerCase, expandIPv6, every, groups.slice, join, map, g.toString, addr.split, head.split, addr.includes, tail.split, fill, Array, Math.max, slice, toString, Number.parseInt
Environment references: c.env.env · c.env.agent
- L9 · authRoutes.post("/login")
- L18 · authRoutes.post("/logout")
- L25 · authRoutes.get("/session")
- L39 · authRoutes.get("/sessions")
- L41 · authRoutes.delete("/sessions/:id")
- L47 · authRoutes.post("/sessions/revoke-others")
- L52 · authRoutes.post("/password")
Environment references: c.env.agent
- L11 · chatRoutes.get("/conversations")
- L16 · chatRoutes.post("/conversations")
- L21 · chatRoutes.get("/conversations/:id")
- L32 · chatRoutes.patch("/conversations/:id")
- L38 · chatRoutes.delete("/conversations/:id")
- L45 · chatRoutes.delete("/conversations")
- L57 · chatRoutes.post("/chat")
- L123 · chatRoutes.post("/chat/cancel")
- L131 · chatRoutes.post("/actions/:id/approve")
- L139 · chatRoutes.post("/actions/:id/cancel")
- L151 · chatRoutes.get("/conversations/:cid/task-plans/:id")
- L155 · chatRoutes.patch("/conversations/:cid/task-plans/:id")
- L160 · chatRoutes.post("/conversations/:cid/task-plans/:id/confirm")
- L167 · chatRoutes.post("/conversations/:cid/task-plans/:id/cancel")
Environment references: c.env.agent
- L33 · dataRoutes.get("/tasks")
- L40 · dataRoutes.post("/tasks")
- L46 · dataRoutes.get("/tasks/:id")
- L48 · dataRoutes.patch("/tasks/:id")
- L54 · dataRoutes.delete("/tasks/:id")
- L62 · dataRoutes.get("/memories")
- L72 · dataRoutes.post("/memories")
- L78 · dataRoutes.patch("/memories/:id")
- L84 · dataRoutes.delete("/memories/:id")
- L89 · dataRoutes.delete("/memories")
- L97 · dataRoutes.get("/memories/reviews")
- L108 · dataRoutes.get("/memories/:id")
- L110 · dataRoutes.post("/memories/reviews/:id")
- L115 · dataRoutes.get("/memories/:id/history")
- L117 · dataRoutes.post("/memories/:id/status")
- L123 · dataRoutes.post("/memories/:id/correct")
- L137 · dataRoutes.get("/notes")
- L142 · dataRoutes.post("/notes")
- L148 · dataRoutes.get("/notes/:id")
- L150 · dataRoutes.patch("/notes/:id")
- L156 · dataRoutes.delete("/notes/:id")
- L163 · dataRoutes.get("/projects")
- L165 · dataRoutes.post("/projects")
- L170 · dataRoutes.patch("/projects/:id")
- L175 · dataRoutes.delete("/projects/:id")
- L27 · assertProject calls (conditional paths may differ): c.env.agent.services.projects.get
Environment references: c.env.agent
- L18 · automationRoutes.get("/automations")
- L28 · automationRoutes.get("/automations/:id")
- L32 · automationRoutes.get("/automations/:id/runs")
- L46 · automationRoutes.post("/automations/:id/run")
- L52 · automationRoutes.post("/automations")
- L58 · automationRoutes.patch("/automations/:id")
- L65 · automationRoutes.delete("/automations/:id")
- L72 · automationRoutes.get("/notifications")
- L78 · automationRoutes.post("/notifications/read")
- L85 · automationRoutes.delete("/notifications/:id")
- L12 · denyRemoteAgentPrompt calls (conditional paths may differ): c.get
Environment references: c.env.agent
- L17 · systemRoutes.get("/health")
- L44 · systemRoutes.get("/settings")
- L46 · systemRoutes.patch("/settings")
- L58 · systemRoutes.get("/usage")
- L75 · systemRoutes.get("/system")
- L88 · systemRoutes.get("/system/update")
- L93 · systemRoutes.get("/activity")
- L95 · systemRoutes.get("/export")
- L106 · systemRoutes.post("/import")
Environment references: c.env.agent · c.env.env
- L93 · semanticRoutes.use("/memories")
- L101 · semanticRoutes.use("/memories/*")
- L115 · semanticRoutes.use("/import")
- L173 · semanticRoutes.use("/actions/*")
- L191 · semanticRoutes.use("/actions/*")
- L212 · semanticRoutes.get("/semantic/config")
- L217 · semanticRoutes.put("/semantic/config")
- L239 · semanticRoutes.get("/semantic/status")
- L269 · semanticRoutes.delete("/semantic/data")
- L354 · semanticRoutes.get("/semantic/search")
- L382 · semanticRoutes.post("/semantic/reindex")
- L50 · requireEnabled calls (conditional paths may differ): cachedSemanticConfig, ensureSemanticSchema
- L72 · afterWrite calls (conditional paths may differ): isSuccess, purge, startBackgroundIndex, createSemanticContext, waitUntilOf, console.warn, String
- L84 · writeConfig calls (conditional paths may differ): cachedSemanticConfig, console.warn, String
- L143 · afterItemDelete calls (conditional paths may differ): isSuccess, deleteItemParts, purgeOrphanItemVectors, console.warn, String
- L320 · itemSearchHits calls (conditional paths may differ): map, s.tasks.list, s.notes.search, automationKeywordHits, scanItemVectors, rrf, cosines.keys, s.tasks.get, s.notes.get, s.automations.get, sort, cosines.get, title, out.push, keywordIds.has
Environment references: c.env.agent · c.env.env
- L11 · pushRoutes.get("/push")
- L13 · pushRoutes.post("/push/subscriptions")
- L32 · pushRoutes.post("/push/unsubscribe")
- L39 · pushRoutes.delete("/push/subscriptions/:id")
- L46 · pushRoutes.post("/push/test")
- L54 · deviceLabel calls (conditional paths may differ): test, join, filter
Environment references: c.env.agent
- L14 · createdId calls (conditional paths may differ): json, res.clone, Object.values, Array.isArray
- L34 · recordRequest calls (conditional paths may differ): c.env.agent.services.remote.recordWrite, pop, pathname.split, createdId, REMOTE_ID_PATTERN.test, caller.id.slice, c.env.agent.services.activity.record
Environment references: c.env.agent
- L13 · remoteRoutes.get("/remote/status")
- L15 · remoteRoutes.patch("/remote/status")
- L23 · remoteRoutes.get("/remote/tokens")
- L25 · remoteRoutes.post("/remote/tokens")
- L33 · remoteRoutes.delete("/remote/tokens/:id")
- L42 · remoteRoutes.delete("/remote/tokens")
Environment references: c.env.agent
- L26 · mcpRoutes.use("/mcp/*")
- L28 · mcpRoutes.get("/mcp/servers")
- L38 · mcpRoutes.post("/mcp/servers")
- L43 · mcpRoutes.get("/mcp/servers/:id")
- L48 · mcpRoutes.patch("/mcp/servers/:id")
- L53 · mcpRoutes.delete("/mcp/servers/:id")
- L59 · mcpRoutes.post("/mcp/servers/:id/connect")
- L68 · mcpRoutes.post("/mcp/oauth/callback")
- L73 · mcpRoutes.post("/mcp/servers/:id/disconnect")
- L77 · mcpRoutes.get("/mcp/servers/:id/tools")
- L81 · mcpRoutes.patch("/mcp/servers/:id/tools/:toolId")
Environment references: c.env.agent
- L19 · capture calls (conditional paths may differ): tokenCount
- L33 · meteredBinding calls (conditional paths may differ): Reflect.get, value.bind, estimateTokens, JSON.stringify, steps.push, target.run, capture, createParser, JSON.parse, response.pipeThrough, parser.feed, decoder.decode, controller.enqueue, parser.reset
- L80 · stepUsage calls (conditional paths may differ): estimateTokens, Math.max, modelUsage, tokens
- L68 · isLegacyDefault calls (conditional paths may differ): Array.isArray, Object.keys
- L103 · readSemanticConfig calls (conditional paths may differ): settings.getJson, defaults, Array.isArray, isLegacyDefault, semanticConfigSchema.shape.model.safeParse, semanticConfigSchema.safeParse, floorDefaultsFor
- L143 · mergeSemanticConfig calls (conditional paths may differ): semanticConfigPatchSchema.safeParse, ValidationError.fromZod, defaults, sameFloor, floorDefaultsFor, semanticConfigSchema.safeParse, defined
- L171 · writeSemanticConfig calls (conditional paths may differ): mergeSemanticConfig, readSemanticConfig, ensureSemanticSchema, settings.setJson
- L179 · deleteSemanticConfig calls (conditional paths may differ): settings.delete
- L23 · taskLine calls (conditional paths may differ): presentTask, oneLine
- L32 · noteLine calls (conditional paths may differ): noteParts, trim, slice, oneLine
- L39 · automationLine calls (conditional paths may differ): oneLine
- L51 · selectSemanticItems calls (conditional paths may differ): peekQueryVector, scanItemVectors, hits.push, hits.sort, ctx.services.settings.get, itemLine, lines.push
- L74 · itemSection calls (conditional paths may differ): conservativeEmbeddingTokens, join, kept.push
- L83 · itemLine calls (conditional paths may differ): services.tasks.get, taskLine, services.notes.get, noteLine, services.automations.get, automationLine
- L43 · tableExists calls (conditional paths may differ): db.one
- L48 · semanticSchema calls (conditional paths may differ): tableExists, db.one
- L57 · ensureSemanticSchema calls (conditional paths may differ): db.run, semanticSchema, db.transaction, toISOString
- L80 · semanticTableNames calls (conditional paths may differ): map, db.all
- L91 · dropSemanticData calls (conditional paths may differ): db.all, tables.filter, db.transaction, db.run, semanticTableNames, dropVectorCache, dropItemVectorCache
- L70 · createSemanticContext calls (conditional paths may differ): createEmbedder
- L99 · rrf calls (conditional paths may differ): list.forEach, scores.set, scores.get
- L113 · embedQuery calls (conditional paths may differ): UsageService.day, reserveItemEmbedding, conservativeEmbeddingTokens, itemCaps, reserveEmbedding, estimateTokens, embedder.embed, correctItemTokens, Math.round, console.warn, String
- L133 · queryKey calls (conditional paths may differ): toLowerCase, replace, query.trim
- L143 · queryVector calls (conditional paths may differ): queryKey, cache.get, hit.failed.includes, embedQuery, cache.delete, cache.set, next, cache.keys
- L160 · peekQueryVector calls (conditional paths may differ): queryKey
- L181 · scanVectors calls (conditional paths may differ): normalize, ctx.db.all, rows.map, map, rows.filter, cachedVectors, past.has, all.push, dot, updated.get, all.sort, compareDesc, compareAsc, all.filter, kept.map, taken.has, kept.push, kept.slice
- L213 · hybridSearch calls (conditional paths may differ): ensureSemanticSchema, hasHistoricalIntent, queryVector, scanVectors, ctx.services.memories.search, rrf, vectorHits.map, keywordHits.map, memories.get, ctx.services.memories.get, hits.push, cosines.get, hits.sort, compareDesc, compareAsc, pullInNewer, hits.slice, Math.max
- L254 · pullInNewer calls (conditional paths may differ): hits.slice, Math.max, newerDuplicateCosine, cachedVectors, out.map, vectors.get, out.includes, seen.has, dot, ctx.services.memories.get, isNewer, candidates.push, candidates.sort, compareAsc, out.indexOf, candidates.slice, pulled.has, pullers.has, out.splice, seen.add
- L315 · savedAt calls (conditional paths may differ): format.formatToParts
- L332 · applyM1 calls (conditional paths may differ): map, sort, compareDesc, compareAsc, savedAt
- L366 · selectSemanticMemories calls (conditional paths may differ): runIndexer, hasHistoricalIntent, hybridSearch, selected.set, selected.values, ctx.services.memories.predecessors, ctx.services.memories.list, applyM1, ctx.services.settings.get, newerWinsNote
- L37 · itemUsageToday calls (conditional paths may differ): tableExists, db.one
- L49 · reserveItemEmbedding calls (conditional paths may differ): tableExists, itemUsageToday, Math.round, db.run
- L70 · adjustItemTokens calls (conditional paths may differ): tableExists, db.run, Math.round
- L79 · correctItemTokens calls (conditional paths may differ): adjustItemTokens
- L117 · changedItemRows calls (conditional paths may differ): tableExists, db.all, itemSourceStamp
- L158 · pendingItems calls (conditional paths may differ): contentHash, current, ranked.push, text, check, touchItemSource, itemVectorMeta, changedItemRows, taskMeta.get, checkStamped, itemSourceStamp, taskText, automationMeta.get, automationText, noteMeta.get, stored.every, stored.map, noteParts, parts.entries, byPart.get
- L267 · runItemPass calls (conditional paths may differ): itemPasses.get, startItemPass, Math.max, state.running.then
- L285 · startItemPass calls (conditional paths may differ): itemPassOnce, itemPasses.set, state.running.then, itemPasses.get, itemPasses.delete
- L295 · itemPassOnce calls (conditional paths may differ): ensureSemanticSchema, purgeOrphanItemVectors, pendingItems, pending.slice, Math.max, UsageService.day, candidates.map, conservativeEmbeddingTokens, reserveItemEmbedding, itemCaps, candidates.slice, embedder.embed, batch.map, vectors.some, correctItemTokens, reduce, textTokens.slice, Math.round, map, filter
- L347 · startBackgroundItemPass calls (conditional paths may differ): then, runItemPass, console.warn, String
- L364 · startBackgroundItemBackfill calls (conditional paths may differ): catch, runItemPass, console.warn, String
- L375 · itemPassRunning calls (conditional paths may differ): itemPasses.has
- L32 · scanItemVectors calls (conditional paths may differ): map, vectors.filter, sem.db.all, cachedItemVectors, updated.get, dot, best.push, best.sort, compareAsc, best.filter, kept.slice
- L70 · prepareSurfaceSearch calls (conditional paths may differ): itemPassRunning, startBackgroundItemBackfill, runItemPass, console.warn, String, queryVector
- L89 · additions calls (conditional paths may differ): rrf, hits.map, sort, hits.filter, keyword.has, fused.get, live, out.push
- L107 · taskAdditions calls (conditional paths may differ): scanItemVectors, map, additions, opts.allowedIds.has, sem.services.tasks.get
- L126 · noteAdditions calls (conditional paths may differ): scanItemVectors, map, additions, sem.services.notes.get, noteParts, slice
- L144 · automationKeywordHits calls (conditional paths may differ): ftsTerms, filter, sem.services.automations.list, toLowerCase, terms.some, text.includes
- L155 · automationAdditions calls (conditional paths may differ): scanItemVectors, map, additions, sem.services.automations.get
- L33 · additionVectors calls (conditional paths may differ): prepareSurfaceSearch, peekQueryVector, filter
- L40 · upstream calls (conditional paths may differ): ALL_TOOLS.find
- L47 · wrapSearchMemories calls (conditional paths may differ): upstream, ctx.services.settings.get, hybridSearch, hits.map, savedAt
- L79 · wrapSearch calls (conditional paths may differ): upstream, base.execute, ctx.services.settings.get, hybridSearch, hits.map, savedAt, additionVectors, map, taskAdditions, result.tasks.map, presentTask, noteAdditions, result.notes.map, automationKeywordHits, keyword.slice, automationAdditions, keyword.map, automations.push, presentAutomation
- L116 · wrapIndexAfter calls (conditional paths may differ): upstream, base.execute, startBackgroundIndex
- L135 · wrapListTasks calls (conditional paths may differ): upstream, base.execute, additionVectors, localDayBounds, map, ctx.services.tasks.list, slice, taskAdditions, result.tasks.map, added.map, presentTask
- L170 · wrapSearchNotes calls (conditional paths may differ): upstream, base.execute, additionVectors, noteAdditions, result.results.map, added.map
- L196 · semanticTools calls (conditional paths may differ): wrapSearchMemories, wrapSearch, wrapIndexAfter, wrapListTasks, wrapSearchNotes, ALL_TOOLS.map
- L26 · estimatedEmbeddingTokens calls (conditional paths may differ): texts.reduce, estimateTokens
- L34 · conservativeEmbeddingTokens calls (conditional paths may differ): ch.codePointAt, Math.ceil
- L102 · fnv1a32 calls (conditional paths may differ): encoder.encode, Math.imul
- L141 · createEmbedder calls (conditional paths may differ): settings.get
- L152 · encodeVector calls (conditional paths may differ): view.setFloat32, String.fromCharCode, btoa
- L161 · decodeVector calls (conditional paths may differ): atob, view.setUint8, binary.charCodeAt, view.getFloat32
- L171 · cosine calls (conditional paths may differ): Math.sqrt
- L190 · contentHash calls (conditional paths may differ): crypto.subtle.digest, encoder.encode, join, Array.from, padStart, b.toString
- L199 · embeddingsToday calls (conditional paths may differ): db.one
- L207 · reserveEmbedding calls (conditional paths may differ): embeddingsToday, recordEmbeddings
- L213 · recordEmbeddings calls (conditional paths may differ): db.run, Math.round
- L228 · recordEmbeddingCall calls (conditional paths may differ): record, modelUsage, tokens, estimatedEmbeddingTokens
Environment references: env.TALORYS_ENV · env.TALORYS_MOCK_AI · env.AI
- L55 · hasTable calls (conditional paths may differ): db.one
- L60 · normalize calls (conditional paths may differ): Number.isFinite, Math.sqrt
- L72 · dot calls (conditional paths may differ): Number.isFinite
- L83 · toHex calls (conditional paths may differ): view.setFloat32, padStart, toString
- L93 · decodeStored calls (conditional paths may differ): normalize, decodeVector, ArrayBuffer.isView, view.getFloat32
- L108 · convertLegacyVectors calls (conditional paths may differ): hasTable, db.all, db.run, toHex, normalize, decodeVector
- L123 · forget calls (conditional paths may differ): caches.get, cache.vectors.delete
- L132 · upsertVectors calls (conditional paths may differ): rows.map, normalize, db.transaction, db.run, toHex, caches.get, cache.vectors.has, fits, cache.vectors.set, cache.vectors.delete
- L159 · deleteVector calls (conditional paths may differ): hasTable, db.run, forget
- L166 · deleteAllVectors calls (conditional paths may differ): hasTable, db.run, caches.get, cache.vectors.clear
- L176 · purgeOrphanVectors calls (conditional paths may differ): hasTable, map, db.all, forget
- L186 · dropVectorCache calls (conditional paths may differ): caches.delete
- L191 · vectorMeta calls (conditional paths may differ): hasTable, db.all, meta.set
- L201 · vectorMetaFor calls (conditional paths may differ): hasTable, db.one
- L207 · countVectors calls (conditional paths may differ): hasTable, db.one
- L212 · countFor calls (conditional paths may differ): db.one
- L223 · cachedVectors calls (conditional paths may differ): hasTable, caches.delete, countFor, caches.get, db.all, vectors.set, decodeStored, caches.set
- L66 · estimateConsumption calls (conditional paths may differ): Object.hasOwn
- L8 · hasHistoricalIntent calls (conditional paths may differ): PAST_PATTERN.test
- L14 · newEphemeralKeys calls (conditional paths may differ): crypto.subtle.generateKey, crypto.subtle.exportKey, crypto.getRandomValues
- L21 · importEphemeral calls (conditional paths may differ): crypto.subtle.importKey, pointToJwk
- L26 · hkdf calls (conditional paths may differ): crypto.subtle.importKey, crypto.subtle.deriveBits
- L41 · encryptPayload calls (conditional paths may differ): newEphemeralKeys, crypto.subtle.importKey, crypto.subtle.deriveBits, concat, encoder.encode, hkdf, crypto.subtle.encrypt, header.set, setUint32
- L2 · b64url calls (conditional paths may differ): String.fromCharCode, replace, btoa
- L10 · fromB64url calls (conditional paths may differ): test, atob, replace, text.replace, bin.charCodeAt
- L18 · concat calls (conditional paths may differ): parts.reduce, out.set
- L29 · pointToJwk calls (conditional paths may differ): b64url, point.slice
- L67 · quoteTitle calls (conditional paths may differ): trim, text.replace, clean.slice
- L192 · stableStringify calls (conditional paths may differ): JSON.stringify, Array.isArray, join, value.map, sort, Object.entries, a.localeCompare, entries.map, stableStringify
- L200 · targetVersion calls (conditional paths may differ): tool.snapshot, crypto.subtle.digest, encode, stableStringify, join, Array.from, padStart, b.toString
- L212 · runApprovedAction calls (conditional paths may differ): pending.get, tools.find, pending.resolve, pending.view, targetVersion, JSON.parse, tool.input.safeParse, tool.commit, pending.setResult, pending.markFailed, console.error
- L54 · classifyMcpError calls (conditional paths may differ): statusOf, String, test, console.warn, text.slice
- L71 · limitBody calls (conditional paths may differ): Number, res.headers.get, res.body.pipeThrough, controller.error, controller.enqueue
- L105 · guardedFetch calls (conditional paths may differ): headers.delete, auth.allowOrigin, test, headers.get, includes, headers.set, base, limitBody
- L134 · transportFor calls (conditional paths may differ): guardedFetch
- L44 · sameSecret calls (conditional paths may differ): a.charCodeAt, b.charCodeAt
- L56 · slugBase calls (conditional paths may differ): slice, replace, name.toLowerCase
- L61 · shortHash calls (conditional paths may differ): crypto.subtle.digest, encode, join, Array.from, slice, padStart, b.toString
- L66 · parseHints calls (conditional paths may differ): JSON.parse
- L84 · presentTool calls (conditional paths may differ): parseHints
- L22 · stableStringify calls (conditional paths may differ): JSON.stringify, Array.isArray, join, value.map, sort, Object.entries, a.localeCompare, entries.map, stableStringify
- L29 · sha256 calls (conditional paths may differ): crypto.subtle.digest, encode, join, Array.from, padStart, b.toString
- L34 · depth calls (conditional paths may differ): Object.values, Math.max, depth
- L42 · toZod calls (conditional paths may differ): z.fromJSONSchema
- L51 · checkTool calls (conditional paths may differ): JSON.stringify, slice, clip, trim, replace, tool.title.trim, depth, toZod, sha256, stableStringify
- L81 · resultText calls (conditional paths may differ): parts.push, JSON.stringify, trim, parts.join, clip
- L95 · modelResult calls (conditional paths may differ): resultText
- L130 · toToolDefinition calls (conditional paths may differ): host, displayName, runtime.call, modelResult, JSON.stringify, runtime.snapshot, targetId.split, rest.join, resultText
- L27 · privateIPv6 calls (conditional paths may differ): every, groups.slice, privateIPv4, embeddedV4
- L43 · isPrivateAddress calls (conditional paths may differ): toLowerCase, address.replace, ipv4Octets, privateIPv4, expandIPv6, bare.split, privateIPv6
- L58 · validateEndpoint calls (conditional paths may differ): raw.trim, url.hostname.toLowerCase, LOCAL_DEV_HOSTS.has, isIpLiteral, isPrivateAddress, host.includes, PRIVATE_SUFFIXES.some, s.slice, host.endsWith
- L83 · dohResolver calls (conditional paths may differ): fetcher, encodeURIComponent, AbortSignal.timeout, res.json, map, filter, Promise.all, lookup
- L104 · assertPublicHost calls (conditional paths may differ): url.hostname.toLowerCase, isIpLiteral, LOCAL_DEV_HOSTS.has, resolve, addresses.some
- L10 · toDue calls (conditional paths may differ): parseLocalDateTime
- L18 · resolveTask calls (conditional paths may differ): ctx.services.tasks.resolve
- L24 · resolveProject calls (conditional paths may differ): ctx.services.projects.resolve
- L33 · presentTask calls (conditional paths may differ): formatLocal, t.description.slice
- L5 · noteSnapshot calls (conditional paths may differ): ctx.services.notes.get
- L26 · exportData calls (conditional paths may differ): toISOString, s.settings.get, db.all, map, JSON.parse, String
- L81 · importData calls (conditional paths may differ): backupSchema.safeParse, ValidationError.fromZod, s.settings.get, toISOString, db.transaction, db.run, str, pick, iso, db.one, JSON.stringify, map, decodeTools, strOrNull, test, statedDate, importedLinks.push, s.memoryHistory.lifecycle.wouldCycle, projectExists, scheduleSpecSchema.safeParse
- L23 · checkForUpdate calls (conditional paths may differ): Date.now, settings.get, settings.getJson, fetchLatest, settings.setJson, compareVersions, toISOString
- L48 · fetchLatest calls (conditional paths may differ): fetchFn, AbortSignal.timeout, res.json, parseVersion
- L59 · parseVersion calls (conditional paths may differ): exec, v.trim, Number
- L65 · compareVersions calls (conditional paths may differ): parseVersion
- L40 · pendingMemories calls (conditional paths may differ): vectorMeta, db.all, embeddingText, contentHash, meta.get, pending.push
- L56 · purgeOrphans calls (conditional paths may differ): purgeOrphanVectors
- L61 · purgeVector calls (conditional paths may differ): deleteVector
- L66 · purgeAllVectors calls (conditional paths may differ): deleteAllVectors
- L92 · runIndexer calls (conditional paths may differ): passes.get, startPass, Math.max, state.running.then
- L107 · startPass calls (conditional paths may differ): indexOnce, passes.set, state.running.then, passes.get, passes.delete
- L117 · indexOnce calls (conditional paths may differ): ensureSemanticSchema, purgeOrphans, pendingMemories, pending.slice, Math.max, batch.map, reserveEmbedding, UsageService.day, texts.reduce, estimateTokens, embedder.embed, vectors.some, now, upsertVectors, vectors.map, String, console.warn
- L164 · startBackgroundIndex calls (conditional paths may differ): then, runIndexer, console.warn, String
- L178 · startBackgroundBackfill calls (conditional paths may differ): catch, runIndexer, runItemPass, console.warn, String
- L68 · hasTable calls (conditional paths may differ): db.one
- L73 · invalidate calls (conditional paths may differ): caches.get, perType.delete
- L83 · toHex calls (conditional paths may differ): view.setFloat32, padStart, toString
- L92 · fromBlob calls (conditional paths may differ): ArrayBuffer.isView, view.getFloat32
- L102 · upsertItemVectors calls (conditional paths may differ): hasTable, db.transaction, db.run, toHex, normalize, invalidate, rows.map
- L120 · deleteItemParts calls (conditional paths may differ): hasTable, db.run, invalidate
- L127 · deleteAllItemVectors calls (conditional paths may differ): hasTable, db.run, invalidate
- L134 · touchItemSource calls (conditional paths may differ): hasTable, db.run
- L140 · itemVectorMeta calls (conditional paths may differ): hasTable, db.all, meta.get, parts.push, meta.set
- L156 · purgeOrphanItemVectors calls (conditional paths may differ): hasTable, ITEM_TYPES.some, db.one, db.transaction, db.all, touched.push, invalidate
- L182 · countItemVectors calls (conditional paths may differ): hasTable, db.one
- L188 · countIndexedItems calls (conditional paths may differ): hasTable, db.all
- L200 · countFor calls (conditional paths may differ): db.one
- L212 · cachedItemVectors calls (conditional paths may differ): hasTable, caches.delete, countFor, caches.get, db.all, items.get, parts.push, fromBlob, items.set, caches.set, perType.set
- L242 · dropItemVectorCache calls (conditional paths may differ): caches.delete
- L17 · isRemoteAllowed calls (conditional paths may differ): path.split, segments.some, REMOTE_DENIED_SEGMENTS.includes, EXACT.has, COLLECTIONS.has, COLLECTION_METHODS.has, REMOTE_ID_PATTERN.test, ITEM_METHODS.has
- L11 · taskText calls (conditional paths may differ): description.trim, description.slice
- L21 · automationText calls (conditional paths may differ): a.name.trim, a.message.trim, toLowerCase, s.replace, includes, fold, message.slice
- L41 · pack calls (conditional paths may differ): out.push
- L55 · hardSplit calls (conditional paths may differ): parts.push, text.slice
- L65 · chunkNote calls (conditional paths may differ): pack, content.split, raw.trim, paragraphs.push, flush, flatMap, paragraph.split, hardSplit, chunks.slice
- L91 · noteParts calls (conditional paths may differ): n.content.trim, chunkNote, parts.push
- L135 · extractStatedDate calls (conditional paths may differ): text.match, MONTHS.join, padStart, String, MONTHS.indexOf, toLowerCase
Build and deployment pipeline · 2 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
test · no job dependencies declared
- actions/checkout@v4
actions/checkout@v4 - actions/setup-node@v4
actions/setup-node@v4 - Shell command
npm ci - Shell command
npm run lint - Shell command
npm run typecheck - Shell command
npm run build - Shell command
npm test - Shell command
npm run test:pack - Shell command
npx playwright install --with-deps chromium - Shell command
npm run test:e2e - Real-account smoke test
npm run test:smoke
Triggers: push, workflow_dispatch
deploy · no job dependencies declared
- actions/checkout@v4
actions/checkout@v4 - Assemble site
mkdir -p _site cp -r site/. _site/ cp -r docs/screenshots _site/screenshots - actions/configure-pages@v5
actions/configure-pages@v5 - actions/upload-pages-artifact@v3
actions/upload-pages-artifact@v3 - actions/deploy-pages@v4
actions/deploy-pages@v4
build: npm run build -w @talorys/web && npm run build -w @talorys/agent && npm run build -w create-talorys
build: wrangler deploy --dry-run --outdir dist
build: tsc --noEmit -p . && vite build
build: tsup && node scripts/build-templates.mjsprepublishOnly: npm run build
Repository README
View original on GitHub ↗Full upstream document by @rociiu · README.md · snapshot 25781ec
Talorys
Your personal AI agent. Your own cloud.
Website: rociiu.github.io/talorys
Talorys is a free, open-source personal AI assistant that runs entirely inside your own Cloudflare account. It chats with you, remembers what matters, manages tasks, notes and projects, and runs reminders and routines on a schedule — without any server, database or account operated by the Talorys developers.
One person. One Cloudflare account. One command. One personal AI agent.
npx create-talorys@latest
Or install it with Appflare, which deploys it as a single Worker:
1. What Talorys is
- A private assistant — chat with streaming responses, Markdown and tool activity indicators.
- Memory — durable personal facts and preferences you can view, edit and delete. Only the most relevant memories are sent to the model on each turn.
- Semantic recall (optional) — finds memories by meaning as well as keyword, lists them newest first with their saved date, and lets the agent find tasks, notes and automations by meaning. Off by default; turn it on with the Semantic memory recall switch near the bottom of Settings → AI (its other settings appear once it is on), or install with
--semantic. See docs/semantic-recall.md. - Memory history — facts that change over time: when you say you sold your car, Talorys asks you whether the old one is now a past fact, and with the optional AI check a confident answer does it for you. Unclear conflicts also wait for your review. See docs/memory-history.md.
- Tasks, notes and projects — full CRUD in the UI, and from chat ("Add a task to review my project tomorrow"). Chat task due dates reject impossible calendar dates, including timestamps with explicit UTC offsets.
- AI task planning — describe a goal ("Plan the tasks to launch my iOS app next month") and Talorys proposes a plan as an interactive card in chat. Review, edit or remove tasks, pick a project, then confirm: nothing is created until you do, and confirming creates everything at once, exactly once.
- Automations — one-time and recurring reminders, daily task digests and optional AI routines, delivered to an in-app notification center. They run on Durable Object alarms, so nothing has to stay online.
- Push notifications — reminders, digests and routines also arrive as system notifications on the phones and computers you turn them on for, even when Talorys is closed (Settings → General). Uses standard Web Push, encrypted end to end; on iPhone and iPad, add Talorys to the Home Screen first.
- Remote MCP servers (optional) — connect remote MCP servers in Settings → Integrations (no auth, a bearer token, or OAuth sign-in) and choose which of their tools Talorys may use. Tools start off, and calls that may change something wait for your approval. Not used by scheduled routines. See docs/mcp.md.
- Single-user by design — no signup, no accounts, no teams. The installer sets the owner password.
- Works without AI — tasks, notes, memories and reminders keep working if Workers AI is unavailable or your free quota is used up.
Screenshots
| Chat with tool activity | Tasks |
|---|---|
![]() |
![]() |
| Memory (dark mode) | Automations |
![]() |
![]() |

2. How it works
Browser ──HTTPS──▶ Cloudflare Pages (React app + /api Pages Function)
│ service binding (no public URL)
▼
Private Worker (Hono router)
│ getAgentByName("personal-agent")
▼
TalorysAgent — Cloudflare Agents SDK Durable Object
├─ SQLite: conversations, memories, tasks, notes, projects,
│ automations, sessions, settings, usage
├─ Workers AI: @cf/zai-org/glm-4.7-flash (streaming + tool calling)
└─ Alarms: reminders and recurring schedules
- The browser only ever talks to your
*.pages.devsite./api/*requests run a Pages Function that forwards them over a service binding to the agent Worker. - The agent Worker is deployed with
workers_dev: falseandpreview_urls: false: it has no public URL. - Authentication and authorization happen in the Worker, not the frontend.
- Chat responses stream as Server-Sent Events end-to-end.
More detail: docs/architecture.md.
3. Cloudflare services used
| Service | Used for | Free plan |
|---|---|---|
| Cloudflare Pages | Frontend + Pages Function | Yes |
| Cloudflare Workers | Private API Worker | Yes |
| Durable Objects (SQLite) | All data, scheduling alarms | Yes |
| Workers AI | Chat model (@cf/zai-org/glm-4.7-flash) |
Yes, daily allocation |
Talorys does not provision R2, D1, KV, Vectorize, AI Search, Workflows or any paid service.
4. Deploy
npx create-talorys@latest
The installer:
- Checks Node.js (22+) and uses its bundled Wrangler CLI (any globally installed
wrangler/cfis detected but not required). - Verifies your Cloudflare login, or opens Cloudflare's authorization page in your browser.
- Lets you choose an account (if you have several) and an agent name.
- Asks for an owner password (hidden input, strength-checked). It is hashed locally with PBKDF2-SHA256 and stored only as a Cloudflare secret.
- Generates a 256-bit session secret, a Web Push key pair, and unique resource names (
talorys-<id>-agent,talorys-<id>-web). - Deploys the private Worker (creating its SQLite Durable Object), stores secrets, creates and deploys the Pages project with the service binding.
- Verifies the live deployment (frontend, auth endpoint, unauthenticated rejection, storage health) without running any AI inference.
- Prints the real
https://….pages.devURL reported by Cloudflare.
It writes a talorys/ directory containing talorys.json (installation id,
account id, resource names, URL — no secrets) and the deployable artifacts.
Keep it for updates.
Interrupted? Run the same command again in the same place. It reconciles what already exists: no duplicate resources, no password re-prompt if the password is already configured, and no data is ever deleted.
Non-interactive installs: TALORYS_OWNER_PASSWORD=... npx create-talorys@latest --yes --account-id <id>
(with CLOUDFLARE_API_TOKEN set if you are not logged in).
Permissions
Browser login uses Wrangler's standard OAuth flow — no Global API Key. If you use an API token instead, it needs:
- Account › Workers Scripts › Edit
- Account › Cloudflare Pages › Edit
- Account › Workers AI › Read
- Account › Account Settings › Read
- User › User Details › Read (optional; shows your email)
See docs/deployment.md.
5. Access
Open the URL the installer prints, enter your owner password, and start chatting. Sign in from any number of devices — they are all the same owner. Manage sessions under Settings → Security.
Forgot the password? From your talorys/ directory:
npx create-talorys@latest reset-password
This replaces the password secret and signs out every device.
6. Where your data lives
All data is stored in a single SQLite-backed Durable Object (personal-agent)
in your Cloudflare account. Talorys has no telemetry, analytics, tracking or
advertising code and sends nothing to its developers.
Cloudflare processes your data to provide its services — including running Workers AI inference on your chat messages and the relevant memories included in each prompt. Review Cloudflare's privacy policy and Workers AI terms.
If you connect a remote MCP server, calling one of its tools sends that call's arguments to the server, outside your Cloudflare account. Nothing is sent to an MCP server unless you connected it and turned the tool on. See docs/mcp.md.
Security model and threat mitigations: docs/security.md.
Calling the API from scripts with personal access tokens: docs/remote-access.md.
7. What is free and what may cost money
Talorys is designed to fit the Cloudflare Workers Free plan and never enables paid features on its own. It is not "unlimited free", though:
- Free plans have account-level quotas (requests, Durable Object usage, a daily Workers AI Neuron allocation). Quotas are set by Cloudflare and can change.
- When the AI allocation is exhausted, chat shows a clear message and resumes after the daily reset. Everything else keeps working, and your data is untouched.
- If your account is on a paid plan, usage beyond included amounts is billed by Cloudflare under your plan.
Built-in guardrails (adjustable in Settings → AI): max output tokens, max context tokens (older history is summarized), max tool calls and reasoning steps per request, max AI requests per day (every chat-model call counts, including each reasoning step, history summary and memory check), and max scheduled AI runs per day. Embeddings have their own daily request cap. Simple reminders and task digests never use AI.
The Usage panel separates provider-reported tokens from fallback token estimates, and shows local estimated Neurons and USD inference value for today and the last seven days. Model-specific estimates use static published Workers AI rates at the time each call is recorded; the pricing date and source are linked in the panel. They are not a Cloudflare bill or quota balance: account-wide usage and free allowances are excluded. No account credentials or extra network calls are needed for these estimates.
Unsupported models remain unpriced, not free. Mixed coverage is labelled as a partial subtotal, while historical mixed token counts retain an unknown model and provenance and cannot be repriced. Demo/mock calls show estimated tokens but have no real AI consumption and no Neuron or USD estimate. Completed model steps remain counted if a later step fails or is cancelled; steps without a usage receipt cannot be measured. Use the linked Cloudflare dashboard for account-level usage and billing.
8. Local development
Requirements: Node.js 22+.
git clone https://github.com/rociiu/talorys.git
cd talorys
npm install
npm run dev
npm run dev starts the agent Worker (wrangler dev, port 8787), the Pages
Function proxy with its service binding (wrangler pages dev, port 8788) and
the Vite UI (http://localhost:5173) in one terminal. It creates
apps/agent/.dev.vars with a local password (talorys-dev-password, override
with TALORYS_DEV_PASSWORD) and uses the deterministic mock AI provider, so no
Cloudflare login is needed. State persists in .wrangler/state.
Other scripts: npm run build, npm test, npm run test:e2e, npm run test:pack,
npm run typecheck, npm run lint. See docs/development.md.
9. Back up your data
Settings → Privacy → Download backup produces talorys-backup.json with
conversations, memories, tasks, notes, projects, settings and automations
(never sessions or credentials). Import validates the file and merges it in
one transaction; importing the same backup twice is harmless, and a memory,
task, note or project you edited locally after the backup was made is not
overwritten. Memory history (current vs. past facts) is included. Older backups import as before. Settings include
the effective installer defaults. If an imported timezone changes, retained
local automations are also rescheduled in that timezone. A backup file is
treated as untrusted: new automations arrive paused, existing automations are
never changed except to pause them, and the model, limits, demo mode,
auto-memory and memory history settings are kept as they are (the import
result lists any it skipped).
10. Update
From your talorys/ directory:
npx create-talorys@latest update
This redeploys the latest Worker and frontend to the same resources. The
Durable Object namespace is never recreated (migrations are append-only), the
owner password and sessions are kept, and schema migrations run automatically
and transactionally on first request. Also available: status and doctor.
11. Troubleshooting
| Problem | Fix |
|---|---|
| Installer stopped midway | Re-run the same command; it resumes. |
| "did not pass its health checks yet" | New pages.dev sites can take a few minutes. Re-run. |
| Permission errors | Use an account where you can edit Workers and Pages, or a token with the permissions above. |
| Chat says the AI allocation is used up | Wait for the daily reset; or enable Demo mode in Settings → AI. |
| Locked out after failed logins | Wait 15 minutes, or reset the password with the CLI. |
npx create-talorys@latest doctor checks everything automatically. More in
docs/troubleshooting.md.
Repository layout
apps/agent Private Worker: Hono API, TalorysAgent, tools, SQLite repositories
apps/web React + Vite app, Pages Function (functions/api/[[path]].ts)
packages/shared Zod schemas, types, recurrence/DST logic, password hashing
packages/create-talorys The one-command installer (npm package)
tests/e2e Playwright UI tests
tests/smoke Optional real-account deployment test
docs/ Architecture, deployment, development, security, troubleshooting
Credits
Thanks to everyone who has contributed to Talorys:
- Roc Yu (@rociiu) — creator and maintainer
- Derek (@KrunchMuffin) — Node.js 22 requirement, task deletion and cancelled-turn fixes, Windows build support
- Rudy Celekli (@rudycelekli) — automation lifecycle, deployment verification, and portable backup settings fixes
- Andrew B (@who) — opt-in semantic recall for memories
- Mendy Landa (@MendyLanda) — single-Worker deployment
- Michael Tuszynski (@michaeltuszynski) — untrusted backup import handling, daily AI budget accounting, and approvals bound to what was shown
- 김형준 (@wasuphj) — password policy, recurrence date, and vector guard fixes
License
Frequently asked about Talorys
What is Talorys?+
Talorys is a self-hosted ChatGPT alternative built on the Cloudflare developer platform. A personal AI assistant with memory, tasks and reminders in your own Cloudflare account
What does Talorys replace?+
Talorys is listed as an alternative to ChatGPT. Compare the features and tradeoffs before migrating.
What Cloudflare primitives does Talorys use?+
Talorys is built on Durable Objects, Pages, Workers, Workers AI.
How much does Talorys cost to run?+
The README states it is designed to fit the Workers Free plan and does not enable paid services. Chat stops for the day when the Workers AI daily Neuron allocation is exhausted; a paid account is billed for usage above included amounts. Workers AI is metered in Neurons; the free daily allocation limits how much chat a free account gets. The installer generates the production Worker and Pages names; the checked-in agent config is the development template it is derived from. Semantic recall is optional and off by default. Source and configuration review establishes a deployment path and conditional costs; this candidate was not executed or load-tested. Official Cloudflare pricing pages saved 31 Oct 2026 (Markdown editions of developers.cloudflare.com pricing documentation). Check current Cloudflare pricing before deploying.
Is Talorys open source?+
The upstream repository declares the MIT license. Read its terms at https://raw.githubusercontent.com/rociiu/talorys/25781ec26283b0bf3694f9f6d433d34e19fafd16/LICENSE. Source code and contributor credit are available at https://github.com/rociiu/talorys.
Community rating
No ratings yet. Tried this project? Share your experience.
One rating per verified account. You can change or remove yours. Accounts are email verified; use of the software is self-reported.



Discussion · 0
sign in to comment →