All notable changes to Maschina will be documented here.
Format: Semantic Versioning — [version] YYYY-MM-DD
- API unit tests —
services/api/src/routes/health.test.ts,auth.test.ts,agents.test.tscovering Layer 1 (401 auth rejection) and Layer 2 (400 validation errors) without DB/network - Plans unit tests —
packages/plans/src/gates.test.ts—isAtLeastTier,nextTier,can.skipBilling,can.useApiKeys,can.useCompliance,can.inviteTeamMembers,hasFeature - Validation unit tests —
packages/validation/src/schemas.test.ts—assertValid,parseBody,ValidationError, all agent + auth + run schemas - Model access integration tests —
packages/model/src/model_access.integration.test.ts— plan tier × model access matrix,resolveModelcascade fallback for all tiers - Rust scheduler unit tests — 14 tests in
services/daemon/src/scheduler/mod.rs#[cfg(test)]module:score()(capacity, load, model match, GPU, stake cap),weighted_random_pick()(empty, single, zero-total, probability distribution) - Python risk tests —
packages/risk/tests/test_risk.pyreplaces placeholder:check_input(injection patterns, length limit),check_output(PII detection),check_quota_pre_run(exhausted, low, unlimited) - Python agent tests —
packages/agents/tests/test_agents.pyreplaces placeholder: AgentType enum, all 5 concrete agents, ABC enforcement, to_dict shape - Python runtime model tests —
packages/runtime/tests/test_runtime.pyreplaces placeholder: RunInput, RunResult, Message, ToolResult validation and serialization - Integration test suite (
tests/integration/) —auth_flow.test.ts(full createApp() middleware stack),security.test.ts(auth bypass, injection payloads, security headers),chaos.test.ts(DB fault, Redis fault, concurrent request handling) - E2E smoke tests (
tests/e2e/smoke.spec.ts) — skipped unlessRUN_E2E=true - k6 load test scripts (
tests/load/) —health.k6.js(ramp 20→100 VUs, p95<100ms),auth.k6.js(constant-arrival + spike, rate limit detection),agents.k6.js(CRUD lifecycle) tests/vitest.config.ts— vitest config for integration/e2e suitestests/tsconfig.json— TypeScript config for tests package
tests/package.json— updated scripts to use vitest config, added hono devDependency- Deleted old placeholder test files (test_agents_placeholder.py, test_risk_placeholder.py, test_runtime_placeholder.py) — replaced with real implementations
services/daemon/src/chain.rs— on-chain receipt anchoring: computes deterministic SHA-256 payload hash, builds Anchoranchor_receiptinstruction (discriminator + borsh args), derives receipt + pool PDAs, loads authority keypair, submits via Helius RPC; fire-and-forgetservices/daemon/src/orchestrator/analyze.rs— callschain::submit_receipt()after every successful run; converts task price cents → USDC lamports; no-op when CHAIN_ENABLED=falseservices/daemon/src/config.rs— addedhelius_rpc_url,settlement_program_id,solana_authority_keypair,chain_enabledfieldsservices/daemon/src/main.rs— registeredmod chainservices/daemon/Cargo.toml— addedsolana-sdk,solana-rpc-client,borsh
services/api/src/routes/nodes.ts— slash handler now auto-suspends node (status = 'suspended',suspendedAt) when post-slashstakedUsdcdrops below the tier minimum; response includessuspended: trueflagservices/daemon/src/orchestrator/analyze.rs—update_node_reputation()now auto-suspends active nodes whosereputation_scoredrops below 20 with ≥10 tasks of signal; prevents degraded nodes from continuing to receive work
programs/settlement/src/instructions/mod.rs— exports all 5 instruction modulesprograms/settlement/src/instructions/anchor_receipt.rs— AnchorReceipt context + handler: Ed25519 signature verification (stub), receipt PDA init, earnings split (65/20/10/5) accumulated into SettlementPool, ReceiptAnchored event emittedprograms/settlement/src/instructions/deposit_stake.rs— DepositStake: NodeStake + SettlementPool PDAs init_if_needed on first deposit; staked_amount accumulated; StakeDeposited eventprograms/settlement/src/instructions/withdraw_stake.rs— WithdrawStake (7-day lock) + FinaliseWithdrawal (post-lock SPL transfer stub); Clock::get() validationprograms/settlement/src/instructions/slash_stake.rs— SlashStake: bps validation (1–10000), proportional slash from staked_amount, total_slashed tracking; StakeSlashed eventprograms/settlement/src/instructions/settle_earnings.rs— SettleEarnings: drains pool to node/developer/treasury/validators wallets (SPL CPI stub); EarningsSettled eventprograms/settlement/src/lib.rs— added finalise_withdrawal instructionprograms/Anchor.toml— settlement program registered under [programs.localnet]packages/chain/src/settlement.ts— TypeScript Anchor client: PDA helpers (receiptPda, stakePda, poolPda), getSettlementProgram, fetchReceipt, isReceiptAnchored, UUID<->bytes utilitiespackages/chain/src/webhook.ts— Helius webhook processing: HeliusWebhookPayload types, processHeliusWebhook dispatcher, registerSettlementWebhook helperpackages/chain/src/index.ts— exports for settlement + webhook modulespackages/chain/package.json— added @coral-xyz/anchor ^0.30.1services/api/src/routes/webhooks.ts— POST /webhooks/helius: Helius auth header verification, processHeliusWebhook dispatch, onReceiptAnchored handler stubservices/api/src/env.ts— HELIUS_WEBHOOK_SECRET optional env var
services/runtime/src/streaming.py— SSE streaming endpoint for all three runners (Anthropic, OpenAI, Ollama); chunk/done/error event types;POST /streamadded to FastAPI appservices/realtime/src/handlers.rs—POST /internal/run-eventhandler: receives run status + chunk events from daemon and fans out to connected clients via broadcast registryservices/realtime/src/main.rs—/internal/run-eventroute registered
packages/chain/— Helius + Solana foundation: client singleton, wallet validation, Ed25519 ownership verification (buildChallenge + verifyWalletSignature via nacl)services/api/src/routes/wallets.ts— GET /wallets/challenge, POST /wallets, GET /wallets, POST /wallets/verify, DELETE /wallets/:id; Helius + Orb chosen as Solana RPC + usage metering layers- CI + api package.json updated for @maschina/chain
services/node/— newmaschina-nodeRust binary for compute node operators:src/identity.rs— Ed25519 keypair generation + persistence to~/.config/maschina-node/identity.toml;sign()stub for Phase 5 receipt signingsrc/config.rs— env-driven config:MASCHINA_API_URL,MASCHINA_API_KEY,NODE_NAME,NODE_REGION,NODE_INTERNAL_URL,NODE_HEARTBEAT_INTERVAL_SECS,NODE_MAX_CONCURRENT_TASKS,NODE_CONFIG_DIRsrc/api.rs— typed API client:register_node(),submit_public_key(),heartbeat()src/heartbeat.rs— periodic heartbeat loop withActiveTaskCounter+send_once()for startup/shutdownsrc/main.rs— full lifecycle: load/generate identity → register (idempotent) → submit public key → initial heartbeat → heartbeat loop → graceful shutdown
Cargo.toml(workspace) —services/nodeadded to workspace members.github/workflows/ci.yml—maschina-nodeadded to all 4 Rust CI steps (fmt, clippy, test, build)
services/daemon/src/watchdog.rs— standalone watchdog loop that sweepsagent_runsevery 30s for runs stuck inrunningstatus beyond the timeout threshold; force-fails them witherror_code = "watchdog_timeout"; reuses analyze phase for reputation update- realtime notification; race-safe (UPDATE WHERE status = 'running' + rows_affected check)
services/daemon/src/config.rs—watchdog_timeout_secs: Option<i64>field; configurable viaWATCHDOG_TIMEOUT_SECSenv var; defaults to 600sservices/daemon/src/main.rs— watchdog spawned alongside orchestrator + health server;mod watchdogregistered
packages/db/src/schema/pg/enums.ts—stakeEventTypeEnum("deposit" | "withdraw" | "slash")packages/db/src/schema/pg/nodes.ts—publicKey: textcolumn on nodes table (Ed25519 public key hex, 64 chars);nodeStakeEventstable: append-only staking ledger with amountUsdc, balanceAfterUsdc, reason, triggeredBy, slashPct, txSignature columns;NodeStakeEvent/NewNodeStakeEventtype exportspackages/db/src/schema/pg/receipts.ts—nodeSignature: text(Ed25519 sig, nullable) andsigningAlg: text(null until node binary ships; "ed25519" when present) columns on execution_receipts — backward-compatible alongside existing HMAC signature columnservices/api/src/routes/nodes.ts— 5 new endpoints:POST /nodes/:id/public-key— node submits Ed25519 public key (idempotent, supports rotation)POST /nodes/:id/stake— record USDC deposit, updates nodes.stakedUsdc atomicallyPOST /nodes/:id/unstake— stake withdrawal; validates balance stays >= tier minimumPOST /nodes/:id/slash— admin-triggered slash (slashPct%); inserts stake eventGET /nodes/:id/stake— returns balance + stake event history (paginated) Stake minimums enforced: micro=0, edge=100, standard=500, verified=5000, datacenter=25000 USDC
packages/db/src/schema/pg/nodes.ts—nodeEarningstable: append-only per-run earnings ledger with 65/20/10/5 split columns (nodeCents, developerCents, treasuryCents, validatorCents), billing multiplier, token counts, settlement status; indexed by node + statusservices/daemon/src/orchestrator/analyze.rs—record_node_earnings()fires after every successful run;billing_multiplier()lookup by model prefix;task_price_cents()= tokens/1k × 0.2¢ × multiplier + 1¢/run; splits total into 65/20/10/5 and inserts into node_earnings (fire-and-forget)services/api/src/routes/nodes.ts—GET /nodes/:id/earnings: returns per-run earnings rows + totalPendingCents + totalSettledCents; node owner or admin only; filterable by status, paginated
services/daemon/src/scheduler/mod.rs— reputation and stake factored into node scoring:(reputation_score / 100) * 20pts +min(stake / 1000, 1) * 5pts on top of existing load (50) + model match (30) + GPU (20) factors; max score ~125packages/marketplace/src/index.ts— addedcalcExecutionRevenue()with the correct 65/20/10/5 split (node/developer/treasury/validators) for per-execution on-chain task revenue;calcRevenueShare()retained for fiat listing sales (70/30)
packages/billing/src/marketplace.ts—createMarketplacePaymentIntent(): creates a pending order + Stripe PaymentIntent for a paid listing; returns{clientSecret, paymentIntentId, orderId}for Stripe.js confirmationpackages/billing/src/webhooks.ts— handlespayment_intent.succeeded: detectsmaschinaProduct: "marketplace_listing", completes the order, credits seller 70% via credit ledger, increments download count, forks the agent config as a new agent owned by the buyer (idempotent)packages/billing/src/index.ts— exports new marketplace helpersservices/api/src/routes/marketplace.ts:POST /marketplace/listings/:id/buy— creates PI, returns clientSecretGET /marketplace/orders— buyer purchase history with listing namesGET /marketplace/earnings— seller earnings total + transaction list
services/worker/src/worker/workflows/activities.py—run_agent_stepfully wired:- Fetches agent config (system_prompt, model) + enabled skills from DB via asyncpg
- Fetches user plan_tier from subscriptions table
- Generates per-step
run_id(UUID) for runtime tracing - Fixes payload to match
RunRequest:input_payload: {"message": prompt}(was"prompt") - Includes all required fields:
run_id,plan_tier,model,system_prompt,max_tokens,timeout_secs,skills,skill_configs - Returns normalised
{"step_id", "step_run_id", "output", "output_payload", "input_tokens", "output_tokens"}
packages/sdk/ts/tsconfig.json— added"examples"to include array (fixes red files in IDE)
packages/runtime/src/maschina_runtime/tools.py—DelegateAgentTool: delegates a subtask to another agent viaPOST /internal/delegate, returns output synchronously; guards against self-delegationpackages/runtime/src/maschina_runtime/__init__.py— exportsDelegateAgentToolservices/runtime/src/config.py—MASCHINA_API_URL+INTERNAL_SECRETsettingsservices/runtime/src/skills.py—delegate_agentslug wired;build_tools()gainscaller_agent_id+user_idparams for delegation contextservices/runtime/src/runner.py— passescaller_agent_id+user_idtobuild_tools()services/api/src/routes/internal.ts—POST /internal/delegate: secret-gated, fetches target agent config + skills, calls runtime synchronously, returns outputservices/api/src/routes/agents.ts—GET /agents/discover: lists own agents available for delegation (filterable by type)services/api/src/app.ts—/internalroutes registeredpackages/connectors/src/skills.ts—delegate_agentadded to SKILL_CATALOG (access+ tier)services/runtime/tests/test_runner_routing.py—DelegateAgentTooladded to maschina_runtime.tools stub
services/daemon/src/scheduler/mod.rs—select_node()now returns(String, Option<Uuid>)(url + node_id)services/daemon/src/runtime/mod.rs—dispatch()renamed todispatch_to(state, run, node_url)— takes explicit URLservices/daemon/src/orchestrator/execute.rs— calls scheduler separately; passesnode_idtofinalize_runservices/daemon/src/orchestrator/analyze.rs—finalize_runtakesnode_id: Option<Uuid>; firesupdate_node_reputation+update_agent_reputation(tokio::spawn, fire-and-forget) after every run outcome- Node reputation formula:
completed / (completed + failed + timed_out) * 100, clamped 0–100, frozen at current score until 5+ total tasks packages/db/src/schema/pg/agents.ts— addedtotalRunsCompleted,totalRunsFailed,reputationScorecolumnspackages/db/src/schema/sqlite/agents.ts— same columns mirrored for local devpackages/marketplace/src/index.ts—ListingDoc+listingToDoc()gains optionalreputationScorefieldpackages/search/src/indexes.ts— marketplace index:reputationScoreadded to filterable + sortable + displayed attributesservices/api/src/routes/marketplace.ts— publish endpoint fetches agentreputationScoreand includes it in Meilisearch upsert
packages/analytics/src/posthog.ts— PostHog client, lazy-init, typed event helpers (agent.created, agent.run.completed, connector.installed, subscription.upgraded, etc.)\n-packages/analytics/src/langsmith.ts— LangSmith HTTP tracing client (startTrace, endTrace, failTrace)\n-packages/analytics/src/index.ts— package exports\n-packages/analytics/package.json+tsconfig.json— new package\n-services/api/src/routes/analytics.ts— GET /analytics/overview, /runs, /tokens, /agents/top (M5+ gate)\n-services/api/src/app.ts— analytics routes registered\n-services/runtime/src/tracing.py— LangSmith async trace wrapper\n-services/runtime/src/runner.py— start/end/fail trace around every agent run\n\n### Added (2026-03-15 — Connector integrations / feat/connectors)\n-packages/connectors/src/definitions.ts— connector catalog: Slack (OAuth), GitHub (OAuth), Notion (API key), Linear (API key)\n-packages/connectors/src/crypto.ts— AES-256-GCM encrypt/decrypt for credential storage\n-packages/connectors/src/skills.ts— added slack, github, notion, linear to SKILL_CATALOG\n-packages/connectors/src/index.ts— exports crypto + definitions\n-services/api/src/routes/connectors.ts— full connector API: definitions list, CRUD, OAuth flow, incoming webhook receivers (Slack, GitHub, Linear signature verification)\n-services/api/src/app.ts— connector routes registered at /connectors\n-packages/runtime/src/maschina_runtime/tools.py— SlackTool, GitHubTool, NotionTool, LinearTool\n-services/runtime/src/skills.py— slack, github, notion, linear wired into build_tools()
packages/validation/src/schemas/org.ts—CreateOrgSchema,UpdateOrgSchema,InviteMemberSchema,UpdateMemberRoleSchemapackages/db/src/schema/pg/relations.ts—organizationsRelations,organizationMembersRelations,organizationInvitesRelationsservices/api/src/routes/orgs.ts— full org API:POST /orgs,GET /orgs,GET /orgs/:id,PATCH /orgs/:id,DELETE /orgs/:id,GET /orgs/:id/members,PATCH /orgs/:id/members/:memberId,DELETE /orgs/:id/members/:memberId,POST /orgs/:id/invites,GET /orgs/:id/invites,DELETE /orgs/:id/invites/:inviteId,POST /orgs/invites/:token/accept,GET /orgs/:id/agents,GET /orgs/:id/usageservices/api/src/app.ts— org routes registered at/orgs
packages/runtime/src/maschina_runtime/tools.py—CodeExecToolgainsmemory_limit_mb+cpu_limit_secs; appliesresource.setrlimit(RLIMIT_AS, RLIMIT_CPU, RLIMIT_FSIZE) viapreexec_fnon Unix; no-op on Windowsservices/runtime/src/config.py—SANDBOX_ENABLED,SANDBOX_MEMORY_LIMIT_MB,SANDBOX_CPU_LIMIT_SECSsettingsservices/runtime/src/skills.py— passes sandbox limits from config when constructingCodeExecToolservices/runtime/src/models.py—RunResponsegainssandbox_type: str | None("subprocess_rlimit"|"subprocess"|None)services/runtime/src/runner.py— setssandbox_typein response based on active skills + platformservices/daemon/src/runtime/mod.rs—RunOutputgainssandbox_type: Option<String>services/daemon/src/orchestrator/analyze.rs—persist_successwritessandbox_typetoagent_runstable.env.example— sandbox env vars documented
packages/db/src/schema/pg/enums.ts—agentPermissionEnum:internet_access,code_execution,external_api,file_read,file_write,memory_read,memory_write,send_email,send_webhookpackages/db/src/schema/pg/agents.ts—agentPermissionstable (agent_id, permission, granted_at, granted_by_user_id); unique index on (agent_id, permission)packages/db/src/schema/pg/relations.ts—agentPermissionsRelations;agentsRelationsgainspermissionsmany-relationservices/daemon/src/error.rs—PermissionDeniedvariantservices/daemon/src/orchestrator/evaluate.rs—check_skill_permissions():code_exec→code_execution,web_search/http_fetch→internet_access; blocks run if permission absentservices/api/src/routes/permissions.ts—GET/PUT /agents/:id/permissions,DELETE /agents/:id/permissions/:permissionservices/api/src/app.ts— permission routes registered
services/api/src/routes/receipts.ts—POST /receipts/:id/verify: re-derives HMAC-SHA256 from stored payload and confirms signature matches; returns{ valid: boolean, receiptId }.env.example—PROOF_SECRETdocumented with generation hint
packages/db/src/schema/pg/receipts.ts—execution_receiptstable (run_id, agent_id, user_id, node_id, model, input_tokens, output_tokens, payload, signature, issued_at)packages/db/src/schema/pg/relations.ts—executionReceiptsRelations;agentRunsRelationsgainsreceiptmany-relationservices/daemon/src/receipt.rs— HMAC-SHA256 signing: canonical JSON payload (sorted keys) → hex signature;issue_receipt()inserts receipt post-run (non-fatal)services/daemon/src/orchestrator/analyze.rs—issue_receipt()called afterpersist_successon every completed runservices/daemon/src/config.rs—proof_secretfield (env:PROOF_SECRET, dev fallback)services/daemon/Cargo.toml—hmac,sha2,hexdepsservices/api/src/routes/receipts.ts—GET /receipts/:id,GET /agents/:agentId/receipts; ownership-gated per userIdservices/api/src/app.ts— receipt routes registered
packages/flags/src/flags.ts— flag registry (marketplaceEnabled,workflowsEnabled,memoryEnabled,proofOfComputeEnabled,nodeRegistrationEnabled,distributedComputeEnabled,machTeamPlanVisible,billingEnabled,skillMarketplaceEnabled,pluginsEnabled,newRunUiEnabled);FlagNameunion typepackages/flags/src/types.ts—FlagContext(userId, orgId, tier, email, attributes),FlagValue,FlagKeypackages/flags/src/client.ts—FlagClient.is()/FlagClient.all();getFlags(ctx)— LaunchDarkly (lazy) → Redis cache (TTL 60s) → defaults;isEnabled()convenience helperpackages/flags/src/flags.test.ts— tests: defaults, overrides, all(), flag shape.github/workflows/ci.yml—@maschina/storageand@maschina/flagsadded to TS build chain in both ts-typecheck and ts-test jobs
packages/storage/src/client.ts—StorageClient: upload, uploadJson, download, downloadJson, delete, presignedDownload, presignedUpload, publicUrl; CloudFront URL rewriting; MinIO-compatible viaS3_ENDPOINT; singletongetStorage()packages/storage/src/keys.ts—StorageKeys: agentArtifact, taskOutput, upload path helperspackages/storage/src/storage.test.ts— 12 tests (StorageKeys + publicUrl)packages/storage/package.json—@aws-sdk/client-s3+@aws-sdk/s3-request-presignerservices/api/src/routes/storage.ts—POST /storage/upload-url,GET /storage/download-url,DELETE /storage/object; ownership-gated per userIdservices/api/src/app.ts— storage routes registeredservices/api/Dockerfile— storage package built in image
packages/db/src/schema/pg/workflows.ts—workflows+workflowRunstables;workflowTypeEnum+workflowRunStatusEnumadded to enumspackages/events/src/types.ts—WorkflowRunQueued+WorkflowRunCancelledsubjects + data typesservices/worker/src/worker/workflows/— Temporal workflow + activities:agent_workflow.py—AgentWorkflow(@workflow.defn): sequential, parallel, conditional execution strategiesactivities.py—run_agent_step(calls runtime /run),update_run_status(writes to DB)temporal_worker.py— Temporal worker runner (task queue:maschina-workflows)
services/worker/src/worker/handlers/workflow.py— NATS handler that starts Temporal workflow via clientservices/worker/src/worker/main.py—asyncio.gather(run_consumer(), run_temporal_worker())services/worker/pyproject.toml— addedtemporalio>=1.7services/worker/src/worker/config.py— addedtemporal_url+runtime_urlservices/api/src/routes/workflows.ts— full workflow API (CRUD + trigger + run status + cancel)services/api/src/app.ts— registered workflow routes at/workflows
packages/marketplace/src/index.ts—calcRevenueShare()(70/30 split),listingToDoc()(Meilisearch shape),generateSlug()packages/db/src/schema/pg/marketplace.ts—agentId(nullable FK) +agentConfig(jsonb snapshot) onmarketplace_listings; fullmarketplaceOrders+marketplaceReviewstablesservices/api/src/routes/marketplace.ts— full marketplace API: browse (Meilisearch + DB fallback), get listing+reviews, create/update/publish/unpublish listing, fork agent (copies config snapshot), submit review (requires completed order), seller's own listingsservices/api/src/app.ts— registered marketplace routes at/marketplaceservices/api/package.json— added@maschina/marketplaceworkspace dep
services/daemon/src/scheduler/mod.rs— scored node selection replacing naive "most recent heartbeat" strategy- Queries all active nodes with fresh heartbeat in one JOIN (nodes + node_capabilities + latest heartbeat via LATERAL)
- Scoring: load factor (50pts, proportional to spare capacity), model match (30pts, prefix match on supported_models), GPU bonus (20pts, for ollama/* models on GPU nodes)
- Nodes at full capacity (
active_task_count >= max_concurrent_tasks) are excluded from selection - Falls back to
RUNTIME_URLwhen no nodes available or all at capacity; logs capacity state at DEBUG
services/daemon/src/runtime/mod.rs— replacedselect_node_url()withcrate::scheduler::select_node(state, model); removed dead NodeRow structservices/daemon/src/main.rs— registeredschedulermodule
packages/db/src/schema/pg/agents.ts—agent_skillstable (agent_id, skill_name, config, enabled)packages/connectors/src/skills.ts— skill catalog:http_fetch(access+),web_search(m1+, Brave API),code_exec(m5+, sandboxed subprocess); tier gating helperscanUseSkill(),listSkills()packages/connectors/src/index.ts+package.json— wired up, deps fixed to@maschina/planspackages/runtime/src/maschina_runtime/tools.py—WebSearchTool(Brave Search API, max_results configurable),CodeExecTool(subprocess sandbox, timeout configurable, 30s max);HttpFetchToolgains optional domain allowlistservices/runtime/src/skills.py— maps skill slugs → Tool instances; reads BRAVE_SEARCH_API_KEY from envservices/runtime/src/models.py—RunRequestgainsskills: list[str]+skill_configs: dictservices/runtime/src/runner.py— builds tools fromreq.skillsand passes toAgentRunner(Anthropic only)services/daemon/src/orchestrator/scan_compat.rs—JobToRungainsskills: Vec<String>+skill_configsservices/daemon/src/orchestrator/scan.rs— initializes empty skills onJobToRunservices/daemon/src/orchestrator/evaluate.rs— queriesagent_skillstable, populatesrun.skills+run.skill_configsbefore executeservices/daemon/src/runtime/mod.rs—RuntimeRequestgainsskills+skill_configs, passed through to Python runtimeservices/api/src/routes/skills.ts—GET /skills(catalog),GET /agents/:id/skills,PUT /agents/:id/skills/:slug,DELETE /agents/:id/skills/:slug; tier-gated on upsertservices/api/src/app.ts— skill routes registered.env.example—BRAVE_SEARCH_API_KEYadded
packages/vector/src/collections.ts— addedagent_memorycollection (1536-dim Cosine)services/runtime/src/memory.py— episodic memory: retrieve top-k similar memories (Qdrant + OpenAI text-embedding-3-small), store output after each run; all errors swallowed gracefullyservices/runtime/src/runner.py— retrieve memories before routing to LLM, inject as memory block in system prompt; store output memory after runservices/runtime/src/config.py— addedqdrant_url,qdrant_api_key,memory_enabled,memory_top_ksettingsservices/runtime/pyproject.toml— addedqdrant-client>=1.9dependencyservices/api/src/routes/memory.ts—GET /agents/:id/memory(scroll with pagination),DELETE /agents/:id/memory(clear all); ownership-gated, graceful on Qdrant unreachableservices/api/src/app.ts— memory routes registered under/agentsapps/docs/api-reference/agents.mdx— Agent Memory section added (list + clear endpoints)
services/api/src/routes/nodes.ts— full node management CRUD:POST /nodes/register,POST /nodes/:id/heartbeat,GET /nodes,GET /nodes/:id,PATCH /nodes/:id,DELETE /nodes/:idservices/daemon/src/runtime/mod.rs—select_node_url()queriesnodestable for most-recently-healthy active node (heartbeat <60s), falls back toRUNTIME_URLon miss or errorservices/api/src/app.ts—/nodesroute registered
packages/search/src/indexes.ts— agents index settings updated:typeandstatusadded to searchable/filterable/displayedAttributespackages/search/src/search.test.ts— unit tests for INDEXES structure, module exports, client singletonservices/api/src/routes/search.ts—GET /searchroute: auth-scoped, supportsq,type,limit,offset; graceful Meilisearch degradation (returns empty result on unreachable)services/api/src/routes/agents.ts— Meilisearch sync on agent create/update/delete (fire-and-forget, non-blocking)services/api/src/app.ts—/searchroute registeredservices/api/src/index.ts—ensureIndexes()called at startup (non-fatal on failure)services/api/package.json—@maschina/searchadded as dependency
packages/model/src/catalog.ts— TypeScript model catalog: 3 Anthropic cloud models + 3 Ollama local models, per-tier access gates, billing multipliers (Haiku 1x, Sonnet 3x, Opus 15x, Ollama 0x)packages/model/src/index.ts— Barrel exportpackages/model/src/catalog.test.ts— 20 vitest tests covering multipliers, tier access, validation, resolutionpackages/model/tsconfig.json+ build script — TS package alongside existing Python codepackages/validation—RunAgentSchemagains optionalmodelfieldpackages/jobs—AgentExecuteJobgainsmodel+systemPromptfields;dispatchAgentRunupdatedservices/api— Model access validation at run dispatch; resolves system prompt fromagent.config.systemPrompt; passes model + system prompt through job queueservices/daemon—AgentExecuteJob,JobToRungainmodel+system_prompt;RuntimeRequestnow sends all fields the Python runtime needs (plan_tier,model,system_prompt,max_tokens,timeout_secs); URL fixed from/execute→/runservices/daemon—RunOutput.payloadrenamed tooutput_payloadto match PythonRunResponseservices/runtime— Full model routing inrunner.py: routes by model ID prefix (ollama/* vs Anthropic), applies billing multiplier, lazy-imports Anthropic client per request; drops global Ollama flagservices/runtime/tests/test_runner_routing.py— Unit tests for multiplier + routing helpers (no real LLM calls)- CI + pytest scripts updated to include
services/runtimetests
- Daemon was calling
/executeendpoint on Python runtime — correct endpoint is/run - Daemon
RuntimeRequestwas missingplan_tier,model,system_prompt,timeout_secsfields that the PythonRunRequestmodel requires
- All 31 TS packages now build clean (
pnpm turbo build --filter='./packages/*') packages/cache/src/client.ts— ioredis ESM default import via(Redis as any)constructor castpackages/cache/src/ops.ts—import type { Redis as RedisType }(named type import)packages/validation/src/schemas/agent.ts— agent type enum updated to match DB:signal/analysis/execution/optimization/reportingservices/api/src/env.ts— dotenv loaded viaconfig()at module load; Stripe keys optional with empty-string defaultservices/api/src/routes/auth.ts—validatePasswordStrengthreturnsPasswordValidationobject; check.validnot truthiness;verifyPasswordargs were swapped (fixed toverifyPassword(hash, plain))services/api/src/middleware/auth.ts—resolveAuthacceptsHeadersobject; was incorrectly passing raw string; fixed toc.req.raw.headersservices/api/src/routes/billing.ts—/billing/portalnow returns 400 forinternal/accesstier users (no Stripe customer)- All API route files — changed
from "drizzle-orm"→from "@maschina/db"for consistent single-instance resolution packages/telemetry/src/sdk.ts—PeriodicExportingMetricReadermoved to@opentelemetry/sdk-metricspackages/nats/src/streams.ts—readonly string[]spread to mutable arraypackages/plans/src/gates.ts—?? nullforPlanTier | nullreturn
packages/cli/src/tui.rs— ratatui TUI launcher (maschinawith no args):- Horizontally centered panel (64% width) with rounded borders
- Service status panel: api (:3000), gateway (:8080), realtime (:4000), runtime (:8000), daemon
- Status detection: PID file check → port check → stopped
- PID files at
~/.local/share/maschina/pids/ - Auto-refresh every 3s; message banner expires after 5s
- Keys:
↑↓/jknavigate,sstart,xstop,astart all,Xstop all,rrefresh,q/Escquit - Workspace detection: start/stop only available when in Maschina project root
- Log files written to
.maschina/logs/<service>.log - Monochrome palette: White/Gray/DarkGray, no color theme
packages/cli/src/main.rs—commandis nowOption<Commands>;Nonelaunches TUI
packages/cli— complete rewrite ofmaschinaCLI:maschina setup— interactive wizard: API URL, email/password or API key, credential validation, project.maschina/initmaschina login/maschina logout— auth commandsmaschina status— connection + account infomaschina doctor— config, connectivity, and project health checksmaschina agent list|deploy|stop|run— agent management with correct API typesmaschina keys list|create|revoke— API key managementmaschina usage— quota usage with visual bar chartmaschina logs <run_id>— run inspection--jsonglobal flag for scripting/CI output--profile <name>for multi-environment configs (each profile:~/.config/maschina/<profile>.toml)arg_required_else_help = true(shows help instead of panicking on no args)src/output.rs— unified human/JSON output layersrc/project.rs—.maschina/config.tomlper-project config (name, description, agent defaults, runtime URL)
install.sh— curl installer:- Detects OS (macOS/Linux) and architecture (x86_64/aarch64)
- Checks required dependencies (curl, tar)
- Downloads release binary from GitHub
- Installs to
/usr/local/binor~/.local/bin - Adds to PATH in shell rc file (zsh/bash/fish)
- Runs
maschina setupautomatically if interactive TTY
packages/cli/src/services.rs— shared service management module (PID files, port checks, probe, start/stop, log paths);start_svcchecks~/.local/share/maschina/bin/first (installed), falls back to workspace dev modepackages/cli/src/tui.rs— complete rewrite: borderless two-zone launcher (Services + Menu), deimos-style centeredRect,▸cursor, spinner animation, responsive column collapse, Tab/j/k/s/x/l/Enter/r/q controls; returnsLaunchTargetfor post-TUI dispatchpackages/cli/src/commands/service.rs—start/stop/restart/status/logsusing sharedservices::module;--jsonsupportpackages/cli/src/commands/setup.rs— 5-step interactive wizard: connection → account (login/register/paste) → AI providers (MultiSelect: Anthropic/OpenAI/Ollama/OpenRouter/Gemini/Mistral) → database (SQLite/Postgres/Neon) → workspace initpackages/cli/src/commands/agent.rs— addedruns()(run history table) andinspect()(detailed agent view)packages/cli/src/config.rs— addeddb_url: Option<String>andmodel_providers: Vec<ModelProvider>packages/cli/src/main.rs— full rewrite: comprehensive command tree (setup/login/logout/status/doctor/service/agent/keys/models/usage/logs/update/code/config); TUI launcher on no-args;LaunchTargetdispatchinstall.sh— service binary download step: downloadsmaschina-services-<os>-<arch>.tar.gzto~/.local/share/maschina/bin/; graceful fallback if not in release
packages/cli/src/commands/login.rs—Configstruct initializers now includedb_urlandmodel_providersfieldspackages/cli/src/config.rs— defaultConfiginload()now includesdb_url: None, model_providers: vec![]packages/cli/src/commands/agent.rs—AgentRunstruct now hasstarted_at: Option<String>
- Kill commands in root
package.json:kill:api,kill:gateway,kill:realtime,kill:runtime,kill:daemon,kill:all "type": "module"added to all TS packages missing it (ESM consistency)- Dotenvy env loading in
services/gateway,services/realtime,services/daemonRust services
packages/api-client— shared fetch client (api.get/post/patch/put/delete),ApiError,tokenhelpers (localStorage); base URL fromVITE_API_URLpackages/query— TanStack Query hooks:useAgents,useAgent,useAgentRuns,useCreateAgent,useUpdateAgent,useDeleteAgent,useRunAgent,useLogin,useRegister,useLogout,useForgotPassword,useResetPassword,useSubscription,useCredits,useCreateCheckout,useCancelSubscription,useKeys,useCreateKey,useRevokeKey,useUsageSummary,useUsageEvents,useMe,useUpdateMe,useUserspackages/ui/src/index.ts— full barrel export of all 55 shadcn components- All 6 web apps (
app,auth,admin,console,developers,web) — added@maschina/api-clientand@maschina/queryworkspace deps - All 6 web apps —
src/lib/api.tsreplaced with re-export from@maschina/api-client(eliminates duplication) - All 6 web apps —
__root.tsxwrapped with<TooltipProvider>from@maschina/ui apps/docs— switched from Docusaurus to Mintlify;mint.json+introduction.mdx+ full doc structure.github/workflows/deploy.yml— fixed invalidpush_tag:event (movedtags: ["v*"]underpush:)- All 6 web apps —
tsconfig.app.json+tsconfig.node.json— added"composite": true; removed deprecated"baseUrl": "." packages/tsconfig/package.json— added.json-suffixed exports to fix@maschina/tsconfig/node.jsonresolutionapps/*/src/routeTree.gen.ts— stub files for TypeScript compile before firstpnpm devapps/desktop/src-tauri/icons/— full icon set:icon.icns,icon.ico, Windows Store tiles, 32/128/256px PNGsapps/mobile/android/— adaptive icons (foreground PNG + black background XML), all mipmap densitiesapps/mobile/android/wear/— Wear OS module:MainActivity,WearViewModel,WearApp,WatchStore, Compose UIapps/mobile/ios/MaschinaWatch/— watchOS app:WatchStore(WCSession), agents list, status view, 5 complication families
packages/db/src/schema/pg/— full PostgreSQL schema (20 modules): enums, users, auth, organizations, plans, subscriptions, api_keys, usage, credits, agents, audit, jobs, billing_events, compliance, webhooks, notifications, connectors, marketplace, misc, indexpackages/db/src/schema/sqlite/— full SQLite mirror schema (8 modules): users, auth, plans, subscriptions, api_keys, usage, agents, compliance, index; all pg-incompatible types replaced (sqliteTable, text enums, integer timestamps/booleans, text UUIDs, text JSON)packages/db/src/client.ts— dual-dialect client: auto-detectsfile:prefix → SQLite (WAL + FK + busy_timeout), else → PostgreSQL (ssl for neon.tech, connection pooling)packages/db/drizzle.config.ts— dialect-aware: points to pg/ or sqlite/ schema dir, separate migrations/pg and migrations/sqlite outputspackages/db/src/index.ts— exports pg schema as canonical types; named re-exports forpgSchemaandsqliteSchemapackages/db/src/rls/policies.sql— full Row Level Security: 3 DB roles (maschina_app, maschina_readonly, maschina_migrate), RLS enabled on 24 tables, user isolation policies viaapp.current_user_idsession variable, BYPASSRLS for migrate rolepackages/db/src/seed/plans.ts— idempotent plan seed usingonConflictDoUpdateon tier; imports from@maschina/plans(canonical definitions)packages/db/src/seed/index.ts— seed runnerpackages/auth/src/types.ts— UserRole, PlanTier, AuthContext, JwtPayload, TokenPair typespackages/auth/src/jwt.ts— HS256 access tokens (15min), refresh tokens (30d) with separate secret, createTokenPair, generateSecureToken, hashTokenpackages/auth/src/password.ts— argon2id (64MiB/3iter/4para), validatePasswordStrength, needsRehashpackages/auth/src/api-key.ts—msk_live_/msk_test_prefix format, SHA-256 hash, timingSafeEqual comparisonpackages/auth/src/session.ts— createSession, rotateSession (delete-on-use prevents reuse), revokeSession, revokeAllSessions, pruneExpiredSessionspackages/auth/src/validate.ts— resolveAuth: auto-detects API key vs JWT from Bearer headerpackages/auth/src/rbac.ts— ROLE_HIERARCHY, PLAN_HIERARCHY, planFeatures, requireRole, requirePlan helperspackages/auth/src/verification.ts— email verification (24h), password reset (1h) single-use tokenspackages/authtests — jwt.test.ts, password.test.ts, api-key.test.ts, rbac.test.tspackages/plans/src/types.ts— PlanTier, PlanLimits, PlanFeatures, PlanConfig, QuotaKey, QuotaStatus typespackages/plans/src/definitions.ts— canonical plan configs (Free/Operator/Pro/Enterprise) with token budgets; PLANS record, getPlan, isValidTierpackages/plans/src/gates.ts— hasFeature, isAtLeastTier, nextTier,can.*helpers, getUpgradeHintspackages/plans/src/quotas.ts— getQuotaStatus, withinQuota, canConsumeQuota, getAllQuotas, QUOTA_LABELS, formatLimitpackages/plans/src/training.ts— GDPR/CCPA training consent config: TRAINING_POLICY_VERSION, consent text, jurisdiction list, per-tier default consent, TrainingDataConfig, PII_CATEGORIES, data retention policypackages/validation/— new package: Zod schemas + sanitization + output projectionsrc/sanitize.ts— sanitizeText, sanitizeSlug, sanitizeFilename, sanitizeUrl, enforceLength; server-side only, no DOMsrc/project.ts— projectUser, projectApiKey, projectSession, projectAgent, projectSubscription; explicit field allowlists, sensitive fields never exposedsrc/parse.ts— parseBody, assertValid, ValidationError; safe Zod wrapper with FieldError formattingsrc/schemas/auth.ts— RegisterSchema, LoginSchema, ChangePasswordSchema, ResetPasswordSchema, OAuthCallbackSchemasrc/schemas/user.ts— UpdateProfileSchema, CreateApiKeySchema, UpdateTrainingConsentSchema, RequestDataExportSchema, DeleteAccountSchemasrc/schemas/agent.ts— CreateAgentSchema, RunAgentSchema, CreateConnectorSchema, CreateWebhookSchema
CLAUDE.md— updated with strict session protocol: mandatory CHANGELOG + decisions.md updates during session.claude/settings.json— added PostToolUse hooks that write to.claude/change-audit.logon every Write/Edit
services/api/— Hono HTTP server (Node.js)src/env.ts— Zod env validation, fails fast on startup with clear errorsrc/context.ts— typed Hono context: RequestUser (id/email/role/tier/sessionId/apiKeyId), Variablessrc/app.ts— Hono app factory with global middleware + all route mounts (exported for tests)src/index.ts— Node.js server entry, graceful SIGTERM/SIGINT shutdown, closes Redis cleanlysrc/middleware/auth.ts— requireAuth (JWT + API key), optionalAuth, requireRole, requireFeaturesrc/middleware/quota.ts— requireQuota(type), trackApiCall (applied to all authed routes)src/middleware/ratelimit.ts— Redis sliding window: authRateLimit (10/min), apiRateLimit (300/min), strictLimit (5/5min)src/middleware/error.ts— global error handler: ValidationError→400, QuotaExceededError→429, unknown→500src/middleware/cors.ts— configurable CORS, strict in productionsrc/routes/health.ts— GET /health, GET /ready (DB + Redis liveness)src/routes/auth.ts— register, login, refresh, logout, verify-email, forgot-password, reset-passwordsrc/routes/users.ts— /users/me profile, sessions list/revoke, training consent, GDPR export, account deletionsrc/routes/agents.ts— agents CRUD + POST /agents/:id/run (quota enforced, dispatches to daemon)src/routes/keys.ts— API key CRUD (full key shown once on creation, never again)src/routes/usage.ts— GET /usage (current period quotas), GET /usage/history (paginated events)src/routes/billing.ts— subscription info, checkout, plan change, cancel, portal, balance, top-up, historysrc/routes/webhooks.ts— POST /webhooks/stripe (raw body, signature verified before processing)src/jobs/reconcile.ts— nightly Redis→PostgreSQL usage reconciliation, standalone runnable
packages/billing/src/pricing.ts— per-action pricing rates in cents ($2.00/1M tokens, $0.01/agent run, $0.10/GB/month), calculateCost(), TOPUP_OPTIONS ($5/$10/$20/$50/$100), MIN_TOPUP_CENTS ($5 minimum matching Anthropic)- 7 plan tiers finalized: Access / Mach-1 (M1) / Mach-5 (M5) / Mach-10 (M10) / Teams / Enterprise / Internal
- Access: $0 — free, local Ollama only, onboarding tier
- M1: $20/mo or $204/yr ($17/mo) — entry paid, Maschina model unlocked
- M5: $60/mo or $600/yr ($50/mo) — main individual tier
- M10: $100/mo or $995/yr (~$83/mo) — power user, compliance + priority support
- Teams: $20/seat/mo or $204/seat/yr ($17/seat/mo)
- Enterprise: custom pricing, contact sales
- Internal: $0, unlimited, Asher + team only, never shown publicly, no billing/quota checks
- Prepaid balance model: $5 minimum top-up, depletes by cent, rolls over indefinitely, manual refund button (no auto-refund)
packages/billing/— full Stripe billing integrationsrc/types.ts— BillingInterval, SubscriptionStatus, CreditPackage, CREDIT_PACKAGES (1M/5M/20M token packs), CheckoutResult, PortalResultsrc/client.ts— Stripe singleton (telemetry disabled)src/customers.ts— getOrCreateStripeCustomer (lazy creation, stored in subscriptions table), updateStripeCustomersrc/subscriptions.ts— createSubscriptionCheckout (Stripe Checkout hosted page), changeSubscriptionTier (immediate proration), cancelSubscription (at period end only), createPortalSession (Stripe Customer Portal for self-serve)src/credits.ts— createCreditCheckout, getCreditBalance, addCredits (ledger + balance in DB transaction), consumeCreditssrc/webhooks.ts— constructWebhookEvent (signature verification), handleWebhookEvent (idempotent via stripeEventId unique), routes: subscription.created/updated/deleted, invoice.paid/payment_failed, checkout.session.completed
packages/cache/— Redis abstraction over ioredis: client singleton with reconnect backoff, typed ops (get/set/del/incr/decr/getJson/setJson/pipeline/multi/publish), TTL helpers (secondsUntilEndOfMonth, currentMonthKey)packages/usage/— full usage metering and quota enforcementsrc/types.ts— UsageEventType, RecordUsageInput, QuotaCheckResult, UsageSummary, RateLimitHeaderssrc/period.ts— UTC calendar-month period helpers (getCurrentPeriod, getPeriodForDate, secondsUntilPeriodEnd)src/keys.ts— Redis key schema:quota:{userId}:{type}:{YYYY-MM}, session/plan cache keyssrc/quota.ts— checkQuota (Redis → PostgreSQL cold-start fallback), incrementQuota (atomic INCRBY + expire), getUsageSummary, buildRateLimitHeaders (X-RateLimit-* headers)src/record.ts— recordUsage (Redis incr + PostgreSQL fire-and-forget), recordModelInference (input+output tokens from model response), recordAgentExecution, recordApiCallsrc/storage.ts— getStorageUsageGb (PostgreSQL snapshot, not Redis), checkStorageQuotasrc/reconcile.ts— reconcileUserUsage, reconcileAllUsers (nightly Redis→PostgreSQL checkpoint sync)src/middleware.ts— QuotaExceededError (429), enforceQuota, enforceAndRecordApiCall
packages/db/src/schema/pg/usage.ts— added inputTokens/outputTokens columns to usage_events; changed rollup index to uniqueIndex (required for ON CONFLICT DO UPDATE upserts); updated comment to reflect Redis-first architecturepackages/db/src/schema/sqlite/usage.ts— added inputTokens/outputTokens columns to match pg schema
packages/auth/src/session.ts—pruneExpiredSessions: changedgt(new Date(), sessions.expiresAt)→lt(sessions.expiresAt, new Date())(correct Drizzle column-first argument order)packages/plans/src/index.ts— corrected.tsextension to.jsin ESM import
- Plan
monthlyModelInferences(call count) →monthlyModelTokens(token budget): Free=0, Operator=500k, Pro=5M, Enterprise=-1 (unlimited). Aligns with how all real AI platforms meter usage.
- Full monorepo scaffold: pnpm + Turborepo + polyglot (TypeScript, Python, Rust)
- Rust workspace: daemon, gateway, realtime, CLI, code tool, SDK
- Python packages: runtime (FastAPI), agents, ml (PyTorch), risk
- TypeScript packages: types, core, db (Drizzle), auth, billing, plans, usage, keys, payments, cache, events, jobs, storage, notifications, webhooks, ratelimit, flags, errors, search, crypto, chain, compliance, reputation, marketplace, treasury, connectors, telemetry, config, content, ui, sdk/ts, sdk/python
- TypeScript services: api, analytics, email
- Rust services: daemon, gateway, realtime
- Python services: worker (Celery)
- Solana Anchor programs: treasury, marketplace
- Docker Compose: PostgreSQL 17 + Redis 7
- Biome for TypeScript/JSON linting and formatting
- Vitest for TypeScript testing, pytest for Python, cargo test for Rust
- GitHub Actions CI: TypeScript + Rust + Python jobs
- Local CI via git hooks (pnpm hooks:install)
- Comprehensive root package.json with 100+ pnpm commands
- Drizzle ORM with dual-dialect support (PostgreSQL + SQLite)
- Billing model scaffold: plans (Free/Operator/Pro/Enterprise), metered usage, API keys
- CLAUDE.md for persistent AI session context (gitignored)
.claude/folder:context.md,decisions.md,session.mdfor full audit trail.env.examplewith all environment variables documented- All TS package stubs:
tsconfig.json,src/index.ts,vitest.config.ts(34 packages) - All Python package stubs:
src/maschina_*/__init__.py - All Rust stubs:
src/main.rs(daemon, gateway, realtime, cli, code),src/lib.rs(sdk/rust) - Rust SDK:
MaschinaError,Agent,AgentType,AgentStatustypes - CLI:
maschina init,agent,keyscommand structure (clap) - Root:
vitest.workspace.ts,rust-toolchain.toml,.python-version,pyproject.toml packages/model: GLM-4 fine-tuning scaffold (train, eval, infer, data, config)packages/dbschemas: users, plans, subscriptions, api_keys, usage_events, usage_rollups, credit_transactions, credit_balances, agents, agent_runspackages/dbclient: dual-dialect PostgreSQL/SQLite with Drizzle