@appport/services

中文文档
0.15.0发布于 2026/11/04ISCTypeScriptES Module近 30 天下载 3587

API 密钥、 耐久的 Webhooks 、 背景任务、 以及 AppPort 应用程序的提供者中立密钥

中文使用文档
文档目录37 节
  1. 创建和运行应用程序
  2. 独立管理主机
  3. 消费者 API
  4. 共享主机应用程序的 FeltDB 实例
  5. 版本、健康、证据和配置转移
  6. 什么是Apport服务?
  7. 见它的行动
  8. 建筑
  9. 依赖方向
  10. 为什么API密钥和AuthPort分开?
  11. 安装
  12. 我如何启用 API 密钥 ?
  13. 我该怎么创造呢?
  14. 应用程序如何认证?
  15. 备选案文1:框架中立的HTTP适配器
  16. 备选案文2: 中间快件
  17. 租户安全
  18. 低级接头符提取
  19. 授权是怎么发生的?
  20. 我该怎么用网球?
  21. 设置
  22. 注册终点
  23. 释放事件
  24. 工人:提供网络呼号
  25. 重试策略
  26. 禁用端点
  27. 我该怎么用耐用的工作?
  28. 设置
  29. 登记处理器
  30. 选中任务
  31. 执行工作(工人)
  32. 经常性工作时间表
  33. 重试策略
  34. 我怎么发通知?
  35. 耐久状态住在哪里?
  36. 秘密怎么办?
  37. 仓库布局

Apport 服务

AppPort Services提供可持久运行的应用能力,它们坐落在AuthPort旁边.

服务集包括API Keys,Jobs, Schedule, Secrets, Webhooks, Files, 以及通知等. 通知由AppPort服务公司储存和交付;它们不是注意管理 层。 Attn可能会消耗它们以分别引起注意.

创建和运行应用程序

对于新的申请:

npx create-appport my-app
cd my-app
npm run dev

生成源仅包含业务处理器. 它不创建HTTP服务器,配置持久性,执行CORS/SSE,运行工作员,发送webhoks,或安装信号处理器.

对于现有应用程序:

在应用程序中安装软件包 :

npm install @appport/runtime

初始化每个功能的 AppPort, 或者只选择应用程序的用途 :

npx @appport/runtime init
npx @appport/runtime init --use api,webhooks,jobs

没有旗帜, init 询问哪些能力可以启用和默认每个是。 这个 --use 表格可用于脚本和 CI.

这就产生了两个应该执行的文件:

  • appport.toml 声明您的应用程序使用的 AppPort 能力 .
  • feltdb.flow 是您的应用程序的权威 FeltDB 合同 。 AppPort的内部模板留在npm包内;应用程序开发者不编辑或导入AppPort存储内部.

合同中的靴子AppPort:

import { appport } from '@appport/runtime';

const app = await appport();

// Only capabilities declared in appport.toml are initialized.
await app.api.keys.createApiKey(/* ... */);

appport() 读取 ./appport.toml 并拥有服务建设、持久性、审计基础设施和生命周期。 打电话 await app.close() 在优雅的关闭期间 获取未申报的能力将产生 CapabilityNotDeclaredError 和必要的声明,使之得以实现.

appport.toml 是权威的申请合同。 它在启动时被解析、验证、正常化并被冻结。 它宣布应用程序的身份、部署和州权力、租赁、HTTP/CORS、API密钥、网页快递、工作类型、事件、授权、可观察性、生命周期和发展默认。 哥哥们 feltdb.flow 作为权威性国家合同被部署到联邦贸易局.

仅包含的遗留文件 use api, (中文(简体) ), use webhooks,以及 use jobs 继续支持。 将其中一项扩展至可收回备份的峡谷合同,使用:

npx @appport/runtime config migrate

应用程序代码提供行为 :

import { appport } from '@appport/runtime';

const application = await appport({
  routes: {
    'POST /invoices': async ({ body, tenantId }) => createInvoice(body, tenantId),
  },
  jobs: {
    'invoice.process': async (job) => processInvoice(job.payload),
  },
});

await application.publish('invoice.created', { id: 'inv-123' });

公众 application.state 财务报告和审定财务报表 application.events API提供提供者中立状态并订阅. 应用程序代码从未通过访问其私人数据库的能力到达.

运行时间拥有稳定管理合同:

/_appport/health
/_appport/overview
/_appport/capabilities
/_appport/events
/_appport/api/keys
/_appport/webhooks
/_appport/jobs

卫生为公用. /_appport/capabilities 是未经认证的发现表面,并返回运行中的应用程序的挂载能力目录。 其他终点遵循合同的授权和租户规则。 事件使用 SSE 用于 GET 并发布域事件 POST; API 键,webhooks, 和工作支持运行时拥有的创建/列表操作.

@appport/services 提供CLI和能力实施,但只进口应用源 @appport/runtime现有应用程序可继续使用 createServices() 从 @appport/services 作为兼容的 API.

以常规命令运行您的应用程序, 例如 npm run dev。通过安装的二进制可操作的 CLI 命令 :

npx @appport/runtime api-key list --tenant acme
npx @appport/runtime webhook list --tenant acme
npx @appport/runtime job list --tenant acme

独立管理主机

您不需要应用程序来操作 AppPort 服务 . serve 启动拥有持久状态的管理主机, ServiceGateway,认证,管理路由器,以及 HTTP 服务器:

npx @appport/services init     # appport.toml + feltdb.flow for this deployment
npx @appport/services serve    # GET /v1/ui, /services, and the other management pages

它的束缚 127.0.0.1:4100 默认; 覆盖到 --host 编号 : --port。状态是相同的持久 FeltDB 部署 。 管理请求使用 AppPort API 密钥进行认证(Authorization: Bearer <secret>)或操作员身份适配器;授权留在AuthBoundry。 见 文件/管理。 。 。 .

AppPort 管理其 FeltDB 运行时依赖性; 消费者不导入 @feltdb/core 或者AppPort的内部商店.

存储器定义 AppPort 能力 租户范围 API 密钥, (中文(简体) ), 持久网点, (中文(简体) ), 长期工作 (包括附表),提供者中立 秘诀 元数据/生命周期和范围解析合同, 持久通知,以及 文件. 遗留的运行时态用途 @feltdb/core (主机副本:0.16.1或后为0.x. 机密材料仍由经授权的提供者提供,从来就不是可耐用的AppPort状态.

            Application
                 │
     ┌───────────┴───────────┐
     │                       │
  AuthPort             AppPort Services
     │                       │
identity/authz        API Keys, Webhooks, Jobs, Secrets
     │                       │
     └───────────┬───────────┘
                 │
              FeltDB

消费者 API

共享主机应用程序的 FeltDB 实例

已经拥有其应用程序的 FeltDB 实例的主机可以运行其中的每一项服务, 而不是让 AppPort 服务打开二审 :

import { createFeltDB } from '@feltdb/core';
import { createServices } from '@appport/services';

const db = createFeltDB({ mode: 'local', namespace: 'invoices', path: './state' });
const services = createServices({ db, application: 'invoices', authorizer: authBoundry });
// Service stores, effect evidence, and services.transaction() all use `db`.

主机保留了所有权: AppPort Services从未关闭过它给过的一个实例. 这个 实例本身的部署模式( e)db.capabilities().deployment用于 模式依赖行为。 使用一份副本 @feltdb/core 对主机和此软件包: 它是 同伴依赖( e)>=0.16.1 <1.0.0),所以主机的复制是服务部使用的,npm安装 没有第二个.

要检查服务对象运行在哪个实例( 本地、 内存或远程) , 请使用 services.db (只读)或 services.usesInstance(db)比较身份; services.ownsDb 实值 false 对于提供主机的实例:

assert.ok(services.usesInstance(db));

版本、健康、证据和配置转移

import { VERSION } from '@appport/services';             // equals package.json "version"
import manifest from '@appport/services/package.json' with { type: 'json' };

const health = await services.health({ tenantId: 'acme' });
// health.status: 'ok' | 'degraded' | 'unconfigured'; health.services.webhooks.state; health.attention.failedWebhookDeliveries

const principal = services.identify({ principalId, principalType: 'user', tenantId, runId: requestId });
await services.invoke('files.write', input, { principal });
const { state, records } = await services.evidence.findByRunId(requestId); // state: 'none' | 'started' | 'completed' | 'failed'

const document = await services.configuration.export({ principal, environment: 'staging' });
const report = await other.configuration.import(document, { principal, dryRun: true });
  • services.health() ——服务状态和计数需要注意,从有界限的询问中得出. 见 医生/保健。 。 。 .
  • services.evidence.findByRunId() ——记录在运行标识下的效果证据(呼叫者的相关标识),以及什么是 started 排行是指坠机后. 见 文件/授权。 。 。 .
  • services.configuration.export() 编号 : import() ——环境之间的非秘密配置;秘密值永远不会离开. 见 文件/配置。 。 。 .
  • webhooks.deliveries.read 其本身的阅读网页传送的能力(invoke('webhooks.deliveries.read', { status: 'failed' }), (中文(简体) ), GET /_appport/webhooks/deliveries) (中文(简体) ).

现有认证的Express应用程序可以按其现有的服务实例挂起所支持的管理运行时间:

import { createManagementRouter, createServices } from '@appport/services';

const services = createServices({ path: '.appport', application: 'invoices', authorizer: authBoundry, credentials: authBoundryCustody });
app.use(createManagementRouter({
  services,
  authority: services.gateway,
  authenticate: (request) => hostAuthentication(request), // identity only
}));

主机拥有认证,AuthBoundry拥有授权,而AppPort Services则在任何效果之前执行AuthBoundry的决定. @appport/服务执行效果. 它不决定允许谁造成这些后果. 见 权力模式, (中文(简体) ), 网络商店安全,以及 就业保障. API 密钥管理需要 apikeys.read, (中文(简体) ), apikeys.create,以及 apikeys.revoke (单位:千美元) apikeys.update, (中文(简体) ), apikeys.rotate,以及 apikeys.audit 用于编辑、轮换和审计),总是使用经认证的租户,并返回仅用于创建和轮换答复的简单文字证书。 见 可构成的管理运行时间 完全合同和独立/嵌入行为。 其他软件包可以通过 : 管理扩展。 。 。 .

出界证书使用只限服务器范围的协议. AppPort 定义了引用,上下文,生命周期,审计,出错,以及召回合同; AuthBoundry 授权和 AppBoundry 解决提供者持有的材料. AppPort船没有解析器,秘密商店,供应商适配器,或政策引擎,并暴露出没有浏览器的认证-值路由.

当主人的监护权是 SecretCustody (单位:千美元)withSecret 加号 store 财务报告和审定财务报表 status, (中文(简体) ), credential.store 收集一个值(授权、审计、版本、在FeltDB中从未重复或坚持)并将其交给主机,以及 credential.status 报告是否存在材料。 主机仍然拥有物质生活的地方;一个只解决器的主机得到 CUSTODY_READ_ONLY。 。 。 .

await secrets.withSecret({
  reference: { secretId, tenantId: 'acme', provider: 'pipedrive' },
  context: { tenantId: 'acme', principalId: 'integration:pipedrive', purpose: 'person.lookup', authorizationRef },
}, async ({ value }) => callProvider(value));

见 外出证书向导 拥有、授权交接、生命周期和故障语义.

见 能力清单 财务报告和审定财务报表 能力授权摘要 综合能力矩阵和挂载状态规则.

合同确定可用的运行时间API:

import { appport } from '@appport/runtime';

const app = await appport({ authorizer: authBoundry, credentials: authBoundryCustody });

// Identity comes from authentication; AuthBoundry authorizes each capability.
const principal = await app.authenticate(request);

// Machine identity (an API key identifies a caller; it carries no scopes)
await app.invoke('apikeys.create', { name: 'server-key' }, { principal });

// Durable outbound notifications, bound to a signing credential in AuthBoundry custody
await app.invoke('webhooks.register', {
  url: 'https://acme.example.com/webhooks',
  events: ['invoice.created'],
  signingCredentialRef: 'credential-ref:whsec_acme',
}, { principal });

// Durable deferred execution; the job runs as this principal and is re-authorized on every run
await app.invoke('jobs.create', {
  type: 'invoice.process',
  payload: { invoiceId: 'inv-123' },
  maxAttempts: 3,
}, { principal });

消费者不需要知道FeltDB在下面存在. 只有申报的能力才得以建立,它们共享一个持久的运行时间.

什么是Apport服务?

Apport 服务答案 :

该应用程序暴露出哪些操作能力?

AuthPort仍然回答身份,认证和授权问题.

见它的行动

完整的示例应用程序可在 examples/services-demo/它表明:

  • API 以租户范围为主的密钥认证
  • 建立具有自动网签和工作意图的持久发票
  • 使用重试和重放的 Webhook 送出
  • 以租赁为基础的货币控制处理背景工作
  • 租户与世隔绝和耐久性

见 examples/services-demo/README.md 五分钟的快速启动.

建筑

Apport 服务使用流程 @feltdb/core 合同语言)作为权威的持久计划. 包裹的内部 appport.flow 是模板,从中 appport init 生成应用程序的权威 feltdb.flow数字 :

API 键垂直 :

  • ApiKeys — API 密钥证书( 密钥仅以 scrypt hash 存储)
  • ApiKeyPrefixes ——高效秘密验证前置查询
  • ApiKeyAuditEvents ——查阅审计线索

垂直 Webhoks :

  • WebhookEndpoints - 已注册的网页接收器
  • WebhookDeliveries - 有重试状态的出货记录
  • WebhookAuditEvents - Webhook生命周期审计线索

垂直任务 :

  • Jobs 具有执行状态和租赁跟踪的个人工作
  • JobSchedules 经常性的工作定义
  • JobAuditEvents 工作执行审计线索

垂直密钥 :

  • Secrets - 租户范围合理的秘密身份和生命周期元数据
  • SecretVersions ——提供商参考和明确轮换版本
  • SecretAuditEvents - 创建、解决、轮换、撤销、退休和失败审计记录

秘密行动区分 describeSecret (仅元数据)来自 resolveSecret (授权提供者解决)。 机密值、 平白文本、 已解密的材料和提供者证书不是 appport.flow 或审计活动.

所有藏品均通过租户范围 tenant_id 字段为 tenant_idx 用于高效查询.

依赖方向

feltdb.flow (generated authoritative application contract)
       ↓
TypeScript implementation
       ↓
FeltDB (@feltdb/core, the host's copy)

流程合同在测试时进行解析和验证。 TypeScript商店(FeltDbApiKeyStore,FeltDbWebhook EndpointStore等)直接针对FeltDB收藏执行合同语义.

为什么API密钥和AuthPort分开?

API 密钥认证机器主机. 没错 没有 替换 AuthPort 授权.

API key
   ↓
machine principal
   ↓
AuthPort authorization
   ↓
resource/action decision

安装

npm install
npm run build

@feltdb/core 是一个对等依赖:在 Services 旁安装( 最近的 0.x 发布; npm 7 和 服务处使用这个副本:

{
  "peerDependencies": {
    "@feltdb/core": ">=0.16.1 <1.0.0"
  }
}

我如何启用 API 密钥 ?

用 FeltDB 的真正部署模式创建服务 :

import { createApiKeyService } from '@appport/services';

const service = createApiKeyService({
  mode: 'local',
  namespace: 'appport-services',
  path: './.feltdb/appport-services'
});

远程 FeltDB 部署可以使用相同的导出部署字段 resolveFeltDBDeployment() 明白吗.

我该怎么创造呢?

// `operator` is a verified principal (host authentication or an existing API key).
const created = await service.createApiKey({ name: 'production' }, operator);

console.log(created.secret); // only returned once

API 键可以识别一个呼叫器及其应用. 它没有瞄准镜: AuthBoundry决定 打电话者可以做什么。 通过 scopes 以 迁移错误文件/授权) (中文(简体) ).

可以给出密钥 限制,这个天花板只缩小什么 AuthBoundry允许,加上一个过期. 密钥可以编辑、旋转并使用 可选重叠,一次或批次撤销一次,并审计:

const key = await services.apiKeys.createApiKey({
  name: 'reporting-worker',
  restrictions: { capabilities: ['files.read'], resources: [{ type: 'file', attributes: { owner: 'reporting' } }] },
  expiresAt: new Date(Date.now() + 90 * 86_400_000),
}, operator);

const next = await services.apiKeys.rotateApiKey({ id: key.id, overlapSeconds: 300 }, operator); // next.secret shown once
await services.apiKeys.updateApiKey({ id: next!.id, name: 'reporting-worker-v2' }, operator);
const events = await services.apiKeys.listApiKeyAuditEvents({ credentialId: next!.id }, operator);
await services.apiKeys.revokeAllApiKeys({ createdBy: '[email protected]' }, operator);

撤销和限制更改适用于每次的下一个操作 进程。 见 文档/按键 对于许可模式, 旋转语义,校验键,出错,和迁移注释.

CLI( 通过 APPPORT_AUTHORITY 或者说 APPPORT_API_KEY:

APPPORT_AUTHORITY=./authority.mjs appport api-key create --tenant tenant-123 --name production

应用程序如何认证?

备选案文1:框架中立的HTTP适配器

对于使用普通节点HTTP请求/响应语义的应用程序:

import { createApiKeyAuth } from '@appport/services';

const auth = createApiKeyAuth({ service });

// Optional authentication (returns null if missing/invalid)
const principal = await auth.authenticate(request);

// Required authentication (throws if missing/invalid)
const principal = await auth.require(request);

适配器提取 Authorization: Bearer <api-key> 页眉并返回一个 AuthenticatedPrincipal数字 :

interface AuthenticatedPrincipal {
  principalId: string;
  principalType: 'api_key';
  tenantId: string;
  applicationId: string;
  credentialId: string;
  verifiedBy: 'api_key';
}

校长有品牌:服务只接受校长 认证路径, 绝不是对象字符串或演员字符串 .

备选案文2: 中间快件

对快件应用程序:

import { apiKeyAuth, requireApiKeyAuth } from '@appport/services';

const app = express();

// Optional authentication
app.use(apiKeyAuth(service));

app.get('/invoices', async (req, res) => {
  const principal = req.auth; // null if missing/invalid
  if (!principal) {
    return res.status(401).json({ error: 'Unauthenticated' });
  }
  // Handle request with principal
});

// Or, require authentication
app.use(requireApiKeyAuth(service));

app.get('/protected', async (req, res) => {
  const principal = req.auth; // guaranteed, or middleware rejects
  // Handle request with principal
});

中间器件将本金附在 req.auth ,并通过 req.authContext。 。 。 .

租户安全

如果您的申请独立接受租户情况,请对照委托人确认:

import { assertTenant } from '@appport/services';

assertTenant(principal, tenantIdFromRequest); // throws if mismatch

低级接头符提取

关于自定义框架:

import { authenticateBearerToken } from '@appport/services';

const principal = await authenticateBearerToken(
  authorizationHeader,
  service,
);

授权是怎么发生的?

认证返回一个身份 。 授权属于 AuthBoundry: every 服务效果由配置授权 ServiceAuthorizer 摆在它面前 证书只有在决定之后才能解决,证据是 写给 FeltDB 。 见 文件/授权。 。 。 .

我该怎么用网球?

Webhooks为外部的HTTP端点提供可持久签名的发送.

设置

import { createServices } from '@appport/services';

const services = createServices({ path: './.appport', application: 'invoices', authorizer: authBoundry, credentials: authBoundryCustody });
const service = services.webhooks;

注册终点

const endpoint = await service.createWebhookEndpoint({
  url: 'https://customer.example.com/webhooks',
  events: ['invoice.created', 'invoice.paid'],
  // The signing secret lives in AuthBoundry custody; the endpoint stores only the reference.
  signingCredentialRef: 'credential-ref:whsec_customer',
}, principal);

// Private, loopback, link-local, and metadata destinations are rejected; redirects are never followed.
// See docs/WEBHOOK-SECURITY.md.

释放事件

await service.emitWebhookEvent({
  type: 'invoice.created',
  payload: { id: 'inv-456', amount: 100 }
}, principal);

// Delivery records created automatically for matching endpoints

工人:提供网络呼号

const delivery = await service.getWebhookDelivery(tenantId, deliveryId);
const result = await service.deliverWebhook(tenantId, deliveryId);

if (result.success) {
  // Delivered
} else {
  // Failed or queued for retry
}

交货由HMAC-SHA256签署. 每次交付包括:

{
  "id": "delivery-uuid",
  "eventId": "event-uuid",
  "type": "invoice.created",
  "data": { "id": "inv-456", "amount": 100 },
  "timestamp": "2026-09-10T22:30:00Z"
}

页眉 : X-AppPort-Signature: <hex-encoded-hmac-sha256>

申请阅读 webhooks.deliveries.read (每拨电话最多200次)

const failed = await services.invoke('webhooks.deliveries.read', { status: 'failed', limit: 50 }, { principal });
const one = await services.invoke('webhooks.deliveries.read', { id: deliveryId }, { principal });

消费者核查:

import crypto from 'node:crypto';

function verify(payload, signature, secret) {
  const expected = Buffer.from(crypto.createHmac('sha256', secret).update(payload).digest('hex'));
  const actual = Buffer.from(signature);
  return expected.length === actual.length && crypto.timingSafeEqual(expected, actual);
}

重试策略

  • 第2条 已交付
  • 408 / 429 / 5xx / 网络故障 + 重试,使用有边界的指数回放
  • 3⁄4 ̄ ̧漯b → 失败( 最终) : 从未遵循过方向
  • 其他 4xx (中文(简体) ) 失败( 最终)
  • 拒绝授权 — 失败; 提供者从未被调用
  • AuthBundry 不可用/ 超时 ——以后再审;提供者从不叫
  • 最大尝试 − 5 (可配置)

无法执行可手动重放 :

await service.replayWebhookDelivery(tenantId, deliveryId, principal); // webhooks.replay

禁用端点

await service.disableWebhookEndpoint({ id: endpoint.id }, principal); // webhooks.remove

// No new deliveries created; existing pending deliveries blocked

网络呼号是 至少一次交货消费者应当治疗 eventId 编号 : deliveryId 作为一能识别符.

我该怎么用耐用的工作?

工作提供可靠的背景任务执行,提供再试支助,通过租赁进行货币控制,以及从工人倒闭中自动恢复.

设置

import { createServices } from '@appport/services';

const services = createServices({ path: './.appport', application: 'invoices', authorizer: authBoundry });
const service = services.jobs;

登记处理器

service.register('invoice.process', async (job, execution) => {
  const { invoiceId } = job.payload as { invoiceId: string };
  // The job runs as its durable principal; every effect is authorized again.
  await services.invoke('notifications.send', { recipient: 'finance', type: 'invoice.processed', title: invoiceId }, { principal: execution.principal });
});

选中任务

const job = await service.enqueue({
  type: 'invoice.process',
  payload: { invoiceId: 'inv-456' },
  maxAttempts: 3,
  delegationId: 'del_quote_agent', // optional AuthBoundry delegation, checked on every run
}, principal);

没有永久校长的工作从未被执行,被撤销的代表团 停止下一个运行。 见 医生/。 。 。 .

执行工作(工人)

const result = await service.executeJob(tenantId, jobId, 'worker-1');
if (result) {
  // Job completed successfully
}

经常性工作时间表

const schedule = await service.scheduleRecurring({
  type: 'invoice.reconcile',
  payload: { batchSize: 100 },
  interval: '1h',
}, principal);

重试策略

  • 成绩 工作标志完成
  • 失败 + 用指数反转重试(2^ attremptCount)
  • 最大尝试超过 工作标记失败
  • 工人租赁期满 • 有资格由另一名工人恢复的工作
  • 手动重试 将失败的工作重置为待决状态

工作是 持久和专有. 工作状态在重新启动过程中幸存下来;工人通过基于版本的乐观锁定要求工作.

我怎么发通知?

通知是指通过一个或多个送货渠道传递的持久应用事件. 申请决定发生了什么。 AppPort Services决定如何交付持久通知.

Application       = meaning
AppPort Services  = delivery infrastructure
FeltDB            = durable state and evidence
Attn              = attention and judgment
const { notification, deliveries } = await app.notifications.notify({
  tenantId: 'tenant-a',
  recipient: 'user-1',
  type: 'monitor.triggered',
  title: 'Status changed',
  source: { type: 'monitor', id: 'monitor-7', eventId: 'observation-123' },
  channels: ['browser', 'in-app'],
}, principal);

await app.notifications.markRead('tenant-a', notification.id, recipientPrincipal);
await app.notifications.acknowledge('tenant-a', notification.id, recipientPrincipal);
  • 每个频道都有自己的持久交付记录(pending, (中文(简体) ), delivered, (中文(简体) ), failed, (中文(简体) ), retrying) (中文(简体) ).
  • 重复相同的源事件返回相同的通知.
  • 现有就业基础设施的回收工作.
  • expiresAt 停止过时的交货,但保留记录作为证据.
  • 证书(密码、饼干、授权信头、信使、API密钥、私人密钥)被拒绝.
  • 关闭浏览器不会销毁通知 。 浏览器是一个传送通道,一个新的 会话追赶从 GET /notifications?unread=true。 。 。 .

电子邮件、移动推取、短信和网络呼取频道是注册在 registerChannel()。 。 。 . 见 文件/通知.md 用于资源模型、生命周期、HTTP API、, 和敏感数据边界.

耐久状态住在哪里?

Apport 服务公司通过主机直接在 FeltDB 收藏中存储所有状态 @feltdb/core (0.16.1或后为0.x).

feltdb.flow (generated Flow contract)
      │
      ▼
AppPort Services
      │
      ├─→ FeltDbApiKeyStore (ApiKeys, ApiKeyPrefixes, ApiKeyAuditEvents)
      ├─→ FeltDbWebhookEndpointStore (WebhookEndpoints, WebhookAuditEvents)
      ├─→ FeltDbWebhookDeliveryStore (WebhookDeliveries)
      ├─→ FeltDbJobStore (Jobs, JobSchedules, JobAuditEvents)
      └─→ Secrets protocol (Secrets, SecretVersions, SecretAuditEvents)
      │
      ▼
 @feltdb/core (the host's copy)
      │
      ▼
 real FeltDB

秘密怎么办?

对于 AppPort Secrets,边界为:

application .flow → capability contract → AuthBoundry authorization → secret provider

申请书 secrets ,但从不将暗中值输入 .flowAppPort只公布身份、生命周期、租户、审计和提供商参考元数据; AppBoundry 提供商在AuthBoundry授权后执行.

AppPort是宣告性的:它定义了"秘密能力"的含义. AppBoundry决定如何运行,AuthBoundry决定谁可以使用. 保密合同没有规定提供商、储存、缓存、注射或授权执行.

  • 以密码随机性生成
  • 创建时返回一次
  • 持续前用碎屑
  • 从未存储在耐用 API- key 记录
  • 不包括在审计记录中

仓库布局

  • appport.flow ——用于生成消费者的内部包模板 feltdb.flow 合同
  • /src/api-keys ——API-键型和语义服务
  • /src/authority - 政策执行点:能力表、经核实的主要负责人、执行背景、AuthBoundry协议、目的地政策、效果证据
  • /src/webhooks - Webhook 模型、服务和签名助手
    • models.ts - 终点、交货、活动合同
    • service.ts - Webhook生命周期和交付协调
    • secrets.ts - 秘密生成、加密、HMAC签名
  • /src/jobs - 工作模式、服务和执行
    • models.ts - 工作和时间表合同
    • service.ts 工作周期、重试和时间安排
    • store.ts ——FeltDB后置存储并乐观地锁定
    • worker.ts - 与投票同时执行工作
  • /src/storage — FeltDB 支持的存储和审计槽
    • api-keys.ts API 密钥存储
    • webhooks.ts — Webhook 端点/交付存储
  • /src/runtime ——HTTP/Express认证适配器和取出 Bearer-token
    • api-keys.ts ——取出持证人证
    • http-adapter.ts - 框架中立的HTTP适配器
    • express-middleware.ts ——快递中件
  • /src/contract - AuthPort主要合同
  • /tests ——节点/脚本测试
    • contract.test.ts ——流量DSL验证和收集验证
    • api-keys.test.ts - 核心服务测试
    • http-adapter.test.ts - HTTP适配器测试(安全、隔离、租户安全)
    • integration-app.test.ts 真正的 HTTP 应用程序固定
    • express-integration.test.ts ——快递中件集成测试
    • webhooks.test.ts - Webhook 生命周期和耐用性测试
    • webhooks-delivery.test.ts - HTTP的交付、签字、重试和货币测试
    • jobs.test.ts 工作周期、时间安排和耐久性测试
    • jobs-execution.test.ts - 同时执行、租赁和回收测试
  • /docs ——建筑说明

版本与发布记录

@appport/services 共发布 30 个版本,首次发布于 2026/09/11;过去 12 个月发布了 30 个版本。

版本发布日期距今
0.15.0latest2026/11/043 天前
0.14.02026/11/034 天前
0.13.02026/11/035 天前
0.12.02026/11/035 天前
0.10.02026/11/025 天前
0.9.12026/10/309 天前

关于 @appport/services 的常见问题

@appport/services 是什么?

API 密钥、 耐久的 Webhooks 、 背景任务、 以及 AppPort 应用程序的提供者中立密钥

如何安装 @appport/services?

在项目目录运行 npm install @appport/services,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@feltdb/core(>=0.16.1 <1.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。它还提供命令行工具 appport-services,可以用 npx appport-services 直接运行。

@appport/services 支持 TypeScript 吗?

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

@appport/services 支持 ESM 还是 CommonJS?

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

@appport/services 可以商用吗?

@appport/services 使用 ISC 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@appport/services 的下载量有多少?

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

@appport/services 还在维护吗?

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