Download the PHP package fissible/attest without Composer

On this page you can find all versions of the php package fissible/attest. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package attest

fissible/attest

Tamper-evident signed evidence chains for application events, with optional public anchoring via OpenTimestamps.

Status: Stable — v1.0+. The public API (types marked @api) follows semantic versioning; see STABILITY.md. Anchoring ships @experimental in 1.x.


What is this, in plain terms?

fissible/attest is a tamper-evident logbook for the important things your software does.

Most applications already log events — "invoice approved," "document published," "permission granted." But ordinary logs and database rows can be quietly edited or deleted afterward, and nobody can tell. If a row says a contract was approved at 3pm Tuesday, you're trusting that nobody changed it since.

attest removes that "just trust us." Every event you record is:

The result is a history you can hand to an auditor, a court, a customer, or your future self and prove it hasn't been tampered with.

Why is that valuable?

Picture a dispute months from now:

A customer insists they never approved a $50,000 contract. Your database has a row saying they did — but that row could have been inserted or edited at any time by anyone with database access, so on its own it proves nothing.

With attest, that approval was signed and chained the instant it happened. If anyone altered it, back-dated it, or slipped in a fake one, verification fails and points at the broken entry. If you also anchored it, you can show it existed before a specific Bitcoin block — so it couldn't have been fabricated after the fact.

Typical uses: audit trails, compliance evidence, security investigations, financial and approval workflows, and anywhere "prove this log wasn't edited" actually matters.

What this is not: this is not artifact/build provenance (Sigstore, SLSA). Those prove where a binary came from. attest proves what your application did, and when.

The boundary, up front: signing happens inside your application, with a key your application holds. So attest is tamper-evident against anyone who can reach the evidence store — a rogue DBA, SQL injection, an edited backup — but not against an attacker who has taken over the application itself and can therefore re-sign a rewritten history. Closing that gap takes external key custody, public anchoring, or both. See What this does not prove for the full table, and note that anchoring is experimental in 1.x.

The 30-second example

Record something important when it happens:

Later — maybe months later — prove the whole history is intact and authentic:

If someone tampers with any stored entry, isVerified() returns false and $result->brokenAtSeq tells you exactly which entry broke.

That's the whole idea. Everything below is detail you can read when you need it.


Install

Requires PHP ^8.2 with the bundled sodium extension (used for Ed25519 signing).

ext-zip is optional, required only for portable bundles (bundle:export, bundle:verify, and the Bundle* classes). Recording, verifying, and anchoring chains never touch it. Without it, opening a bundle for read or write throws BundleSupportUnavailable and the bundle CLI commands exit 1.

Using Laravel? See fissible/attest-laravel for Eloquent storage, Artisan commands, queue-ready anchoring, and a JSONL importer.

Storage adapter contract tests

Packages that provide their own storage backends should run the same contract tests as core. The traits are shipped in src/Testing so adapters can depend on one canonical definition:

Anchoring adapters can likewise use Fissible\Attest\Testing\AnchorClaimStoreContractTests. Both shipped contract-test traits are stable @api test support.

How it works (a layer deeper)

fissible/attest always starts with local integrity, and lets you optionally add a public time anchor on top.

Local integrity (always on)

Each event becomes an envelope: it is Ed25519-signed, stored in canonical JSON form, and linked to the previous envelope by hash. That proves whether a local chain is internally consistent and signed by the keys you expect — no network and no third party required.

Public anchoring (optional)

Anchoring adds an external time and publication signal. Chain ranges are batched into RFC 6962-style Merkle roots, submitted to OpenTimestamps calendars, and later upgraded when a Bitcoin block-header attestation is available.

Anchoring is experimental in the 1.x line: the subsystem is usable and tested, but its PHP API may change in a minor release. It graduates to stable after live-network validation against real OpenTimestamps calendars and Bitcoin confirmations. See STABILITY.md.

Verification levels

Verification can require anything from a local-only receipt up to a full Bitcoin-confirmed attestation. The meaningful levels, weakest to strongest:

Level (AnchorOutcome) Meaning
local_only Signed and chained; no external time proof.
pending Submitted to a calendar; not yet confirmed.
upgraded_no_headers Calendar attestation present; block headers not checked.
remote_header_confirmed Confirmed via a remote explorer — the explorer is part of the trust path.
bitcoin_verified Confirmed against a Bitcoin block header you trust (e.g. your own node).

Only the last two levels are evidence. local_only, pending, and upgraded_no_headers are asserted from the chain's own signed content: a pending OpenTimestamps attestation is just a calendar URI, and an upgraded receipt's block height and Merkle path are never checked unless a header provider is configured. Anyone holding a trusted signing key can record an anchor envelope that satisfies --min-anchor pending or --min-anchor upgraded_no_headers, so those levels add nothing to the tamper-evidence claim below. Treat them as operational states — a receipt exists that attest upgrade can later confirm — not as proof of time. A verification that must bind the chain to an external clock needs remote_header_confirmed or bitcoin_verified, which is where a header check actually happens.

What this does not prove

Local integrity defeats database-only tampering. It does not defeat application compromise, and the difference is entirely about who holds the signing key.

Signing happens in your application process, with a key your application can reach — in fissible/attest-laravel, an Ed25519 seed in app env (ATTEST_SIGNING_KEY_SEED). An attacker who reaches that key can rewrite the whole chain and re-sign every envelope. Verification then passes, because the chain really is internally consistent and really is signed by the key you told the verifier to trust. Nothing visibly breaks.

Attacker capability Local integrity + public anchoring
Edits or deletes stored rows (rogue DBA, SQLi, tampered backup) detected detected
Holds the signing key (app RCE, leaked env, malicious insider with deploy access) not detected back-dating detected
Operator colludes and rewrites the history from scratch not detected detected for anchored ranges

The second and third rows are addressed only by anchoring, which is experimental in 1.x pending live-network validation. So the strongest claim the stable surface supports today is:

Tamper-evident against anyone who can reach the evidence store but not the signing key.

That is a genuinely useful property — it covers the ordinary "someone edited the audit table" dispute — but it is narrower than "nobody can forge this."

To extend the boundary:

Also worth stating plainly: attest proves what your application recorded, not that the record is true. A signed contract.approved envelope proves your application asserted that approval at that point in the chain and that the assertion has not been altered since. Whether the approval itself was legitimate is an application-level question.

Full verifier example (anchored)

The complete shape, requiring a Bitcoin-confirmed anchor and a trusted Ed25519 key:

Use AnchorOutcome::REMOTE_HEADER_CONFIRMED with EsploraHeaderProvider when a remote explorer is acceptable. It is convenient, but weaker than a local Bitcoin Core node because the remote service is part of the trust path.

OpenTimestamps calendars receive nonced commitments rather than raw chain roots. That protects the committed content, but submission timing and IP metadata can still link activity.

Handing evidence to someone else

A bundle is the rich answer — one file carrying a manifest, the chain segment, anchor proof envelopes, OTS receipts, and claimed keys. It needs ext-zip.

You do not need a bundle to hand over verifiable evidence. A chain is already a flat file of signed canonical envelopes, one per line, and verification only ever reads those bytes.

Ship the store directory. With FileChainStore, evidence is plain JSONL under the store root. Copy the root (or just its chains/ directory) and the recipient verifies it as-is:

Same guarantees as any other verification: edit a byte and the chain breaks at the entry that was touched, reported as brokenAtSeq.

One thing a hash chain cannot tell you on its own: a truncated copy still verifies. Drop the last few entries and what remains is a shorter, internally consistent, correctly signed chain — isVerified() is true. So completeness is established out of band, from the range the sender stated: check chainStats->envelopeCount and the tail sequence against what you were told to expect, or verify with an explicit toSeq. This is exactly what a bundle manifest carries for you, and why one exists.

Read the raw bytes yourself. Stores implementing RawChainStore expose the stored canonical bytes directly, which is the supported way to feed evidence into your own transport:

To verify without the CLI, hand any ChainStore to StaticVerifier:

What you give up versus a bundle, and why bundles still exist:

Store directory / raw JSONL Bundle
Chain integrity + signatures yes yes
Needs ext-zip no yes
States its own range, issuer, and note no — out-of-band manifest
Carries anchor proof envelopes and OTS receipts no yes
Carries claimed public keys no yes (informational only)
Single file no yes

Anchored verification still works over a directory handoff, but you must convey the anchor proof envelopes yourself and pass them to Verifier as detachedAnchorEnvelopes — a bundle is simply the packaging that keeps them together.

One caveat if you are assembling a handoff by hand rather than copying a store: the filename convention inside chains/ is an implementation detail of FileChainStore, not a frozen format (unlike the bundle layout, which is frozen as fissible.attest.bundle/v1). Copy the directory rather than constructing filenames, and use bundles when you need to build a handoff from a store that is not file-backed — for example the Eloquent store in fissible/attest-laravel.

Payload Types

Payloads passed to record() accept JSON-native scalars (string, int, bool, null), arrays, objects, plus opaque binary blobs via Fissible\Attest\Envelope\Binary. Example:

Binary blobs are stored in canonical form as {"$binary": "<base64>"} and round-trip stably. The $binary key is reserved — a payload that uses it directly is rejected, so your data can never be mistaken for the binary sentinel. Each blob is capped at 64KB raw; larger artifacts must be stored externally and referenced by URL and sha256 hash.

The total signed canonical envelope size is capped at 64KB; payloads approaching that size will be rejected at record() time.

CLI

bin/attest is a Symfony Console application. Install globally or invoke via Composer:

Commands

Command Description
verify Verify a single chain segment (integrity + signatures + anchor coverage).
bundle:export Export one or more chain segments into a portable .attest.zip bundle.
bundle:verify Verify every chain segment inside an exported bundle.
anchor Submit a chain range to an OpenTimestamps calendar and record the receipt.
upgrade Upgrade pending OTS receipts that now have a Bitcoin block-header attestation.

Exit codes

Code Outcome Notes
0 VERIFIED, or non-verification command success INTEGRITY_VERIFIED_UNTRUSTED with --allow-untrusted also exits 0
1 CLI / configuration / runtime error before a VerificationOutcome Bad options, missing files, invalid arguments
2 INTEGRITY_VERIFIED_UNTRUSTED --allow-untrusted downgrades to 0
3 ANCHOR_BELOW_MIN Anchor exists but is below --min-anchor threshold
4 INVALID_CHAIN / INVALID_SIGNATURE / INVALID_ANCHOR Also: bundle export failure; calendar unavailable (anchor, or upgrade --anchor-id when no calendar could be reached); upgrade --anchor-id with no pending anchor for that id; upgrade --all-pending when every anchor failed and none succeeded
5 PROVIDER_DISAGREEMENT --allow-provider-disagreement downgrades to the strongest passing outcome

Examples

With --trusted-key-file, a plain path (without a key id) registers the key by fingerprint only, so it matches only envelopes whose key_id is that fingerprint. Give the key id to verify chains signed under a human-readable id.

JSON output schema

All commands emit a stable JSON envelope on stdout when --json is passed. The schema identifiers are pinned within the 1.x line; future additions will be additive (no removals or renames within the same schema identifier):

A stored record that cannot be decoded (not JSON, wrong field type, malformed signature encoding) is a verification outcome, not a runtime error: verify and bundle:verify report invalid_chain with broken_at_seq at the position of the unreadable record and exit 4.

For attest.cli.upgrade.v1, warnings[] lists unreachable calendars, and an anchor for which no calendar answered is reported in failed[].

verified and completeness.asserted are two different facts, and a consumer should render them separately. attest.cli.result.v1 carries a constant completeness block:

Added in 1.3.0. Output from earlier versions carries the same format_version without this block, so treat completeness as optional when reading results you did not produce.

A chain proves the entries it covers are intact and in order. It cannot prove they are every event that occurred, for two independent reasons: anything that bypassed the writer never reached the chain to be signed, and both verify (via --from/--to) and bundle:verify (which covers only the range the exporter chose) can be scoped to part of a chain. The block is constant rather than computed — attest cannot detect a bypassed path, and a value that varied would imply it had checked. It does not vary per command either, since scoping applies to both.

Bundles

Bundles are the one feature that needs ext-zip; see Install.

Bundles are ZIP containers (extension .attest.zip) with the following layout:

Members are stored uncompressed for byte-accounting symmetry; the reader enforces per-member size caps, a total size cap, and a compression-ratio guard against bundles produced by other writers.

Trust model: keys claimed inside the bundle are NOT trusted by themselves. Operators must supply trusted public keys via --trusted-key <key_id>=<base64> or --trusted-key-file [<key_id>=]<path> at verification time.

Stability & Versioning

From v1.0.0, fissible/attest follows semantic versioning. The supported public API is the set of types marked @api; anything marked @internal (or unmarked) is implementation detail and may change in any release. The on-disk and interchange formats — canonical envelope JSON, the {"$binary": …} sentinel, the fissible.attest.bundle/v1 bundle, and the attest.cli.*.v1 JSON schemas — are frozen within 1.x (additions are additive; removals or renames require a format-version bump). The CLI contract (commands, options, exit codes, --json schemas) is stable even though the PHP classes under src/Cli/ are internal.

Anchoring is experimental in 1.x. The OpenTimestamps/Bitcoin anchoring subsystem (src/Anchor, src/Headers, src/Merkle) is usable and tested, but its PHP API may change in a minor release; it graduates to stable after live-network validation.

See STABILITY.md for the full surface list and policy.

Documentation

The supported surface is the set of @api-annotated types in src/ — see STABILITY.md for the full list and the wire/format stability guarantees.

License

MIT


All versions of attest with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
ext-sodium Version *
ext-json Version *
paragonie/constant_time_encoding Version ^2.6 || ^3.0
psr/http-client Version ^1.0
psr/http-factory Version ^1.0
psr/http-message Version ^1.1 || ^2.0
symfony/console Version ^6.4 || ^7.0 || ^8.0
symfony/uid Version ^6.4 || ^7.0 || ^8.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package fissible/attest contains the following files

Loading the files please wait ...