承接 fissible/attest-laravel 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

fissible/attest-laravel

Composer 安装命令:

composer require fissible/attest-laravel

包简介

Laravel adapter for fissible/attest: Eloquent storage, console commands, scheduled anchoring, events.

README 文档

README

Laravel adapter for fissible/attest: Eloquent storage, Artisan commands, queue-ready anchoring, and events.

Status: 1.0.0 beta — the public API is frozen for consumer soak and stabilizes at 1.0.0. Built on the stable fissible/attest 1.x core.

What is this, in plain terms?

This package is the Laravel integration for fissible/attest, a tamper-evident logbook for the important things your application does — contract approvals, publishes, permission grants, anything you might one day need to prove happened and prove wasn't edited afterward.

The core library handles the cryptography (each event is signed and chained, so any later tampering is detectable, and batches can be "notarized" against the Bitcoin blockchain to prove when they existed). This package wires all of that into Laravel so you barely have to think about it:

  • Store evidence in your database via Eloquent (attest_envelopes), with correct per-driver write locking on SQLite, MySQL, and PostgreSQL.
  • Record an event with a single facade call.
  • Anchor, verify, export, and audit with Artisan commands — runnable by hand, on a queue, or on a schedule.
  • Import existing append-only JSONL logs into evidence chains without duplicates.

New to attest? Read the core README first for the plain-language "what and why," including the worked dispute example. This page assumes you want it inside a Laravel app.

The 30-second example

Record an event when something important happens:

use Fissible\AttestLaravel\Facades\Attest;

Attest::chain('tenant:5')->record('contract.approved', [
    'contract_id' => 'C-2026-014',
    'approved_by' => 'user:7',
    'amount'      => 50_000,
]);

Later, prove that chain is intact and signed by a key you trust:

php artisan attest:verify --chain=tenant:5 --trusted-key=prod=<base64-public-key>

A clean chain exits 0. A tampered or unsigned chain fails — so "the log wasn't edited" stops being something you ask people to take on faith.

Everything below is detail you can read when you need it.

Install

composer require fissible/attest-laravel
php artisan vendor:publish --tag=attest-config
php artisan vendor:publish --tag=attest-migrations
php artisan migrate

During the 1.0 beta, require the prerelease explicitly (Composer will not select it by default): composer require fissible/attest-laravel:^1.0.0-beta.

Requires PHP ^8.2 and Laravel 11, 12, or 13. (Laravel 13 requires PHP ^8.3; PHP 8.2 is supported on Laravel 11 and 12.) Migrations also auto-load if you do not want to publish them.

Configure

Set the attest database connection and signing key environment variables:

ATTEST_CONNECTION=mysql
ATTEST_SIGNING_KEY_SEED=<base64-32-byte-ed25519-seed>
ATTEST_SIGNING_KEY_ID=station-prod-2026-01

Useful operational defaults:

ATTEST_DEFAULT_CHAIN=tenant:5
ATTEST_DEFAULT_DRIVER=local-only
ATTEST_ANCHOR_QUEUE=anchors
ATTEST_MIN_ANCHOR=local_only

OpenTimestamps anchoring and Bitcoin header verification use optional PSR-18/PSR-7 wiring from core. This package suggests guzzlehttp/guzzle and guzzlehttp/psr7; install them when you want calendar or header-provider commands to create HTTP clients from config.

Record

use Fissible\AttestLaravel\Facades\Attest;

$envelope = Attest::chain('tenant:5')->record('cms.entry.published', [
    'entry_id' => 42,
    'checksum' => 'sha256:abc...',
    'actor_id' => 7,
]);

Each record() opens a per-chain write lock, reads the chain tail, builds an AppendContext, signs the envelope with the configured Ed25519 key, validates the context, persists into attest_envelopes, and dispatches an EnvelopeRecorded event after the transaction commits.

Import JSONL

Use GenericJsonlImporter when an application already has append-only JSONL and wants to replay it into an attest chain without duplicate envelopes:

use Fissible\Attest\Chain\ChainStore;
use Fissible\Attest\Canonical\JcsEncoder;
use Fissible\Attest\Signing\Signer;
use Fissible\AttestLaravel\Import\EloquentImportMarkerTrait;
use Fissible\AttestLaravel\Import\GenericJsonlImporter;
use Fissible\AttestLaravel\Import\JsonlImportContext;
use Fissible\AttestLaravel\Import\JsonlImportOptions;
use Illuminate\Database\ConnectionInterface;

final class UpdaterAuditImporter extends GenericJsonlImporter
{
    use EloquentImportMarkerTrait;

    public function __construct(
        ChainStore $store,
        Signer $signer,
        private readonly ConnectionInterface $connection,
    ) {
        parent::__construct($store, $signer);
    }

    protected function importer(): string
    {
        return 'station.updater.audit.global.v1';
    }

    protected function importMarkerConnection(): ConnectionInterface
    {
        return $this->connection;
    }

    protected function parseLine(string $line, int $lineNumber): ?array
    {
        $decoded = json_decode($line, true, flags: JSON_THROW_ON_ERROR);
        if (! is_array($decoded)) {
            throw new \RuntimeException("Line $lineNumber is not a JSON object.");
        }
        return $decoded;
    }

    protected function chainIdFor(array $parsed, JsonlImportContext $context): string
    {
        return 'updater:global';
    }

    protected function contentHashFor(array $parsed, JsonlImportContext $context): string
    {
        return hash('sha256', JcsEncoder::encode($parsed));
    }

    protected function buildPayload(array $parsed, JsonlImportContext $context): array
    {
        return $parsed;
    }
}

importer() is the durable marker namespace stored in attest_import_markers. Include the logical importer, upstream source/feed identity, and schema version; do not use only the PHP class name. contentHashFor() must return a stable lower-case SHA-256 digest for the logical source record, not a line number or byte offset.

The importer uses ChainStore::append() directly and writes the marker inside the append callback. With EloquentChainStore, the marker and envelope append share one transaction, so a failed append does not strand a marker. Reruns skip existing markers; malformed records fail fast by default. Pass new JsonlImportOptions(continueOnError: true) to collect diagnostics and continue past bad lines.

Station's updater bridge is a consumer-side sidecar over its existing runbook audit JSONL. This package ships only the generic importer primitives.

Anchor

Anchor a known range immediately:

php artisan attest:anchor --chain=tenant:5 --from=1 --to=100 --sync

Dispatch the queueable AnchorPendingBatch instead of anchoring inline:

php artisan attest:anchor --chain=tenant:5 --from=1 --queue=anchors

The package does not auto-register schedules. Add scheduling in your app when you want periodic anchoring:

use Illuminate\Support\Facades\Schedule;

Schedule::command('attest:anchor --chain=tenant:5')->hourly();

For OpenTimestamps:

php artisan attest:anchor \
    --chain=tenant:5 \
    --from=1 \
    --to=100 \
    --driver=opentimestamps \
    --calendar-url=https://calendar.opentimestamps.org \
    --sync

Upgrade pending OpenTimestamps receipts:

php artisan attest:upgrade --chain=tenant:5 --all-pending

Verify

Verify chain integrity and trusted signatures:

php artisan attest:verify --chain=tenant:5 --trusted-key=prod=<base64-public-key>

Require an anchor threshold:

php artisan attest:verify \
    --chain=tenant:5 \
    --trusted-key=prod=<base64-public-key> \
    --min-anchor=local_only

Accepted --min-anchor values are local_only, pending, upgraded_no_headers, remote_header_confirmed, and bitcoin_verified.

Bundles

Export a portable proof bundle:

php artisan attest:bundle:export \
    --chain=tenant:5 \
    --from=1 \
    --to=100 \
    --out=storage/app/tenant-5.attest

Verify a bundle:

php artisan attest:bundle:verify \
    --bundle=storage/app/tenant-5.attest \
    --trusted-key=prod=<base64-public-key> \
    --min-anchor=local_only

Claimed keys included in bundles are informational. They are never trusted automatically by attest:bundle:verify.

Integrity Audit

Audit Eloquent read-side index columns against raw canonical envelopes:

php artisan attest:integrity:audit --chain=tenant:5

Exit codes are 0 for clean, 1 for invalid options, and 4 for detected drift.

Database Support

  • SQLite: single-host / single-writer. Write lock is database-wide.
  • MySQL 8: per-chain GET_LOCK. Multi-writer safe.
  • PostgreSQL 16: per-chain pg_try_advisory_xact_lock. Multi-writer safe.

License

MIT

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 5
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-13

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固