@ariestools/aries-dapp-core

中文文档
0.3.0发布于 2026/10/31LGPL-3.0-onlyTypeScriptES Module近 30 天下载 2917

Aries dapp backend core: the DappActor (forked xl1 actor pattern), process-wide provider locator, pluggable reducer, and the composed daemon that runs the storage server plus actors

原始使用文档
文档目录15 节
  1. Development vs production
  2. Pieces
  3. Production composition (outbound-only)
  4. Publication layout
  5. Local daemon (dev only)
  6. CLI
  7. Reducer module contract
  8. Env (daemon)
  9. Production entrypoint (not the CLI bin)
  10. Least-privilege IAM / R2 notes
  11. Publication safety
  12. Releases, pins, rollback, GC
  13. Incremental high-cardinality publication
  14. Browser-neutral consumer
  15. Opt-in R2 contract test

@ariestools/aries-dapp-core

Dapp backend core: a process-wide provider locator, pluggable reducer, coherent generation publication for S3/R2-backed indexes, and the stock DappActor timer. Designed for outbound-only production indexers (no public inbound API on the compute process) as well as the local aries dapp development fixture.

Development vs production

SurfacePurpose
aries-dapp-server bin + noopReducerLocal development only — embedded s3rver via @ariestools/aries-dapp-serve, memory backing, no real derivation
Project-owned entrypoint + real reducerProduction — inject an S3/R2 client, bind logical roles to buckets/prefixes, publish immutable generations

dapp-serve and the noop daemon remain explicit local tools. Production usage is a project-owned executable that composes this package; dapp-core does not dynamically load arbitrary reducer modules and does not store application authority (signing seeds, Webble controller roots, etc.).

Published state and indexes are rebuildable projections, not authority. Public buckets must not be assumed safe for plaintext private material.

Pieces

  • AbstractActor — create/start/stop lifecycle, non-overlapping timers (firstRunDelay is the real delay before the first pass), abort signal on stop, optional shutdown bound for in-flight work, readiness contract.
  • DappProviderLocator / createDappLocator — process-wide locator. One shared S3 client across roles, or per-role clients for least-privilege principals; logical role → physical bucket + prefix + optional public CDN base. The data role is registered read-only by default (put throws).
  • DappObjectReader / DappObjectWriter / DappBucketStore — capability split: reducers see data as read-only; state/index remain writers. get/getWithMeta/stat/put (metadata + conditional writes)/list*/destroy.
  • publishGeneration / readPublishedHead — fenced publication: create-only generation objects, head-last with conditional-head CAS or injected lease (or unfenced for local s3rver only). Returns { ok: true, … } or { ok: false, conflict }.
  • writeIndexerStatus / readIndexerStatus — durable floor/cursor, completed position, observed source head, generation, success/error timestamps, consecutive failures (CDN-revalidate).
  • DappActor — drives a DappReducer on an interval; optional status publication; optional maxConsecutiveFailures for supervisor restarts.
  • bootDappActors — starts N actors, runs readiness, stops earlier actors if a later one fails.

Production composition (outbound-only)

import { S3Client } from '@aws-sdk/client-s3'
import {
  bootDappActors,
  createDappLocator,
  publishGeneration,
  type DappReducer,
} from '@ariestools/aries-dapp-core'

const client = new S3Client({
  region: 'auto',
  endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
  forcePathStyle: true,
  credentials: {
    accessKeyId: process.env.R2_ACCESS_KEY_ID!,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
  },
})

const locator = createDappLocator({
  client,
  ownClient: true,
  bindings: {
    data: {
      bucket: 'my-dapp',
      prefix: 'data',
      publicBaseUrl: 'https://cdn.example.com/data',
    },
    state: {
      bucket: 'my-dapp',
      prefix: 'state',
      publicBaseUrl: 'https://cdn.example.com/state',
    },
    index: {
      bucket: 'my-dapp',
      prefix: 'index',
      publicBaseUrl: 'https://cdn.example.com/index',
    },
  },
})

const reducer: DappReducer = {
  name: 'my-app-indexer',
  version: '1',
  async reduce({ state, index, signal, logger }) {
    const published = await publishGeneration({
      state,
      index,
      safety: { mode: 'conditional-head' }, // fail closed if provider lacks CAS
      // expectedHeadEtag: prior?.headEtag,
      reducer: { name: 'my-app-indexer', version: '1' },
      source: { cursor: '…', completedPosition: '…', observedHead: '…' },
      stateObjects: [{ key: 'view.json', body: '{}', contentType: 'application/json' }],
      indexObjects: [{ key: 'by-id.json', body: '{}', contentType: 'application/json' }],
      signal,
    })
    if (!published.ok) {
      logger.warn(`publish conflict: ${published.conflict.kind} ${published.conflict.message}`)
      return // do not advance checkpoint on conflict
    }
    logger.info(`published ${published.generation}`)
    return {
      cursor: '…',
      lastCompletedPosition: '…',
      observedSourceHead: '…',
      generation: published.generation,
      generationRoot: published.manifestKey,
    }
  },
}

const actors = await bootDappActors(locator, [{
  name: 'MyIndexer',
  reducer,
  firstRunDelayMs: 1_000,
  reduceIntervalMs: 15_000,
  maxConsecutiveFailures: 10,
  publishStatus: true,
}])

Clients never contact this process. They read head.json, generation objects, and status.json from the public CDN/base URLs. Credentials stay out of published config and logs.

See also src/examples/productionComposition.ts.

Publication layout

state/head.json                          # mutable head (revalidate CDN)
state/status.json                        # indexer status (revalidate CDN)
state/generations/<id>/manifest.json     # immutable
state/generations/<id>/…                 # immutable state objects
index/generations/<id>/…                 # immutable index objects

Head is written last. A crash before head leaves the previous complete generation visible. Checkpoints must not advance past a fully published generation (the actor only records generation after a successful reduce that published head).

Local daemon (dev only)

The composed bin (dist/bin/dappServer.mjs) starts the storage server and a DappActor. aries dapp up spawns it.

CLI

# Empty spine (noop reducer)
aries dapp up

# Real project reducer (ESM .mjs or package export — TypeScript not supported)
aries dapp up --reducer ./dist/indexer.mjs
aries dapp up --reducer ./dist/indexer.mjs --reducer-export myIndex
aries dapp up --reducer @myorg/my-dapp/indexer

# Persist buckets across restarts
aries dapp up --backing disk --data-dir ~/.aries/dapp/data

Example reducer shipped with this package:

aries dapp up --reducer ./node_modules/@ariestools/aries-dapp-core/examples/countFactsReducer.mjs
# monorepo:
aries dapp up --reducer ./packages/dapp-core/examples/countFactsReducer.mjs

Reducer module contract

The module must export a DappReducer:

  • default export, or
  • named export reducer, or
  • any name via --reducer-export / DAPP_REDUCER_EXPORT
// dist/indexer.mjs
export default {
  name: 'my-index',
  version: '1',
  async reduce({ data, state, index, signal, logger }) {
    // read data, publishGeneration / publishIncremental, return progress
  },
}

Local publication should use safety: { mode: 'unfenced' } (or honor DAPP_PUBLICATION_SAFETY, which the daemon sets to unfenced by default). s3rver does not support reliable head CAS.

Env (daemon)

VariableDefaultNotes
DAPP_PORT8801
HOST127.0.0.1
DAPP_BACKINGmemorymemory | disk
DAPP_DATA_DIR~/.aries/dapp/datadisk root
DAPP_REDUCER(noop)path to .mjs/.js or package export
DAPP_REDUCER_EXPORTdefault / reducernamed export
DAPP_REDUCE_INTERVAL_MS5000
DAPP_FIRST_RUN_DELAY_MS0
DAPP_PUBLICATION_SAFETYunfencedset by daemon for project reducers
DAPP_PUBLIC_HOST / DAPP_TLS_*--ssl auto

Under --ssl auto the daemon exports the local CA via NODE_EXTRA_CA_CERTS.

Production entrypoint (not the CLI bin)

Do not run aries-dapp-server or aries dapp up in production. Write a project-owned process that:

  1. Builds an S3Client for R2/S3 (credentials from env/IAM — never logged)
  2. Calls createDappLocator({ client, bindings, ownClient: true })
  3. Implements a DappReducer (import statically — no dynamic CLI load required)
  4. Calls bootDappActors with safety: { mode: 'conditional-head' } inside publishGeneration / publishIncremental
  5. Listens for SIGTERM and stops actors + locator.destroy()

See examples/productionComposition.ts and the composition section above. There is no inbound public listener; clients read head/status/objects from CDN.

Least-privilege IAM / R2 notes

  • Writer (private API): state/index need s3:GetObject, s3:PutObject, and usually s3:ListBucket on the role prefix. Data role: GetObject/ListBucket only (no PutObject). Prefer distinct access keys via createDappLocator({ clients: { data, state, index } }).
  • Public CDN: allow anonymous GET/HEAD only. Deny s3:ListBucket and all Put*/Delete* for the public principal. dapp-core never requires public LIST or PUT.
  • Credentials must not appear in published head/manifest/status or logs.

Publication safety

ModeUse
conditional-headProduction when the provider supports If-Match / If-None-Match
leaseProduction with an application-injected lease/fencing adapter
unfencedLocal s3rver / tests only — no concurrent-writer safety

Releases, pins, rollback, GC

import {
  listReleases, pinRelease, rollbackHead,
  planGarbageCollection, runGarbageCollection,
} from '@ariestools/aries-dapp-core'

await pinRelease(state, 'gen-a', { reason: 'hotfix' })
await rollbackHead(state, {
  targetGeneration: 'gen-a',
  safety: { mode: 'conditional-head' },
  expectedHeadEtag: currentEtag,
})

const plan = await planGarbageCollection({
  state,
  index,
  policy: { mode: 'bounded', retainGenerations: 5, cdnGraceMs: 86_400_000 },
})
await runGarbageCollection(plan, { state, index }, { dryRun: true })

Source data is never GC'd. Active head, pins, and in-flight generations are protected.

Incremental high-cardinality publication

For large sparsely-changing indexes use publishIncremental (CAS bodies + 256-way sharded map). Unchanged keys keep stable cas/sha256/<hash> URLs; each pass writes only changed objects + touched shards + root + receipt.

import { publishIncremental } from '@ariestools/aries-dapp-core'

const result = await publishIncremental({
  state,
  index,
  safety: { mode: 'conditional-head' },
  expectedHeadEtag: prior?.headEtag,
  reducer: { name: 'addr-index', version: '1' },
  indexChanges: [{ key: 'by-source/0xabc', body: frameJson, contentType: 'application/json' }],
})

Browser-neutral consumer

import {
  verifyPublishedView, resolveObject,
} from '@ariestools/aries-dapp-core/consumer'

const view = await verifyPublishedView({
  fetch,
  bases: { state: 'https://cdn.example/state', index: 'https://cdn.example/index' },
})
const one = await resolveObject({
  fetch,
  bases: { state: 'https://cdn.example/state', index: 'https://cdn.example/index' },
  logicalKey: 'by-source/0xabc',
  role: 'index',
})

No Node built-ins or AWS SDK on this subpath.

Opt-in R2 contract test

Ordinary CI does not require R2 secrets. With credentials:

export DAPP_R2_CONTRACT_TEST=1
export R2_ACCOUNT_ID=…
export R2_ACCESS_KEY_ID=…
export R2_SECRET_ACCESS_KEY=…
export R2_BUCKET=…
# optional: R2_PUBLIC_BASE_URL=https://pub-….r2.dev
pnpm xy test @ariestools/aries-dapp-core

See src/spec/r2.contract.spec.ts.

版本与发布记录

@ariestools/aries-dapp-core 共发布 19 个版本,首次发布于 2026/07/21;过去 12 个月发布了 19 个版本。

版本发布日期距今
0.3.0latest2026/10/318 天前
0.2.12026/10/2711 天前
0.2.02026/10/021 个月前
0.1.222026/09/141 个月前
0.1.212026/09/131 个月前
0.1.202026/08/302 个月前

关于 @ariestools/aries-dapp-core 的常见问题

@ariestools/aries-dapp-core 是什么?

Aries dapp backend core: the DappActor (forked xl1 actor pattern), process-wide provider locator, pluggable reducer, and the composed daemon that runs the storage server plus actors

如何安装 @ariestools/aries-dapp-core?

在项目目录运行 npm install @ariestools/aries-dapp-core,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@ariestools/sdk(^9.0)、@ariestools/actor(^2.0)、@ariestools/actor-model(^2.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。它还提供命令行工具 aries-dapp-server,可以用 npx aries-dapp-server 直接运行。

@ariestools/aries-dapp-core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@ariestools/aries-dapp-core 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@ariestools/aries-dapp-core 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=26。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@ariestools/aries-dapp-core 可以商用吗?

@ariestools/aries-dapp-core 使用 LGPL-3.0-only 许可。弱 Copyleft:作为库被调用时通常不要求开源你自己的代码,但修改库本身需以 LGPL 开源;前端打包合并后的适用边界存在不同理解。以上为概括,不构成法律意见,请以许可原文为准。

@ariestools/aries-dapp-core 的下载量有多少?

根据 npm 官方下载统计,@ariestools/aries-dapp-core 在 2026/10/07 – 2026/11/05 这 30 天内被下载 2,917 次,日均约 97 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@ariestools/aries-dapp-core 还在维护吗?

最新版本 0.3.0 发布于 2026/10/31(8 天前);至今共发布 19 个版本,其中过去 12 个月发布了 19 个;首次发布于 2026/07/21。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。