PHPackages                             survos/ark-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. survos/ark-bundle

ActiveSymfony-bundle[Utility &amp; Helpers](/categories/utility)

survos/ark-bundle
=================

Symfony bundle for ARK identifier minting and resolution

2.7.0(3w ago)075MITPHPPHP ^8.4CI passing

Since Mar 10Pushed 2w agoCompare

[ Source](https://github.com/survos/ark-bundle)[ Packagist](https://packagist.org/packages/survos/ark-bundle)[ GitHub Sponsors](https://github.com/kbond)[ RSS](/packages/survos-ark-bundle/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (1)Dependencies (95)Versions (113)Used By (0)

SurvosArkBundle
===============

[](#survosarkbundle)

Symfony bundle for ARK (Archival Resource Key) minting, binding, and resolution.

Requires PHP 8.4+ and Symfony 8.

What it does
------------

[](#what-it-does)

- Mints opaque ARK identifiers via the NOID algorithm (`daniel-km/noid`)
- Stores bindings in a Doctrine `ark_binding` pivot table (one SQL lookup to resolve any ARK)
- Derives deterministic 22-character ARK names from ULIDs
- Auto-mints on `prePersist` for entities implementing `ArkableInterface`
- Redirects `GET /ark/{naan}/{name}` → entity's target URL (301)
- Serves ERC metadata via `?info` query parameter
- Exposes an admin browse entry via `#[EntityMeta]` (requires `survos/field-bundle`)
- Exports/imports bindings as JSONL for backups (requires `survos/jsonl-bundle`)

Why ARK
-------

[](#why-ark)

- ARK Standard:
- ARK Spec (IETF draft):
- N2T Global Resolver:

Get a NAAN
----------

[](#get-a-naan)

Request your own NAAN before going to production:

- Application form: [https://n2t.net/e/naan\_request](https://n2t.net/e/naan_request)
- Registry: [https://n2t.net/e/pub/naan\_registry.txt](https://n2t.net/e/pub/naan_registry.txt)

### Resolver rule for N2T registration

[](#resolver-rule-for-n2t-registration)

Use the template form so N2T constructs the correct path:

```
https://your-domain.org/ark/${content}

```

For ARK `ark:12345/x8rd9`, `${content}` = `12345/x8rd9`, producing `https://your-domain.org/ark/12345/x8rd9` which matches the bundle's route exactly.

### Test ARK

[](#test-ark)

Use `_probe` as the identifier string. The bundle serves `GET /ark/{naan}/_probe`with HTTP 200 independently of any database state, so N2T's periodic checks always pass:

```
ark:YOUR_NAAN/_probe

```

Install
-------

[](#install)

```
composer require survos/ark-bundle
```

Optional:

```
composer require survos/field-bundle   # admin navbar entry for ArkBinding
composer require survos/jsonl-bundle   # ark:export / ark:import commands
```

Configuration
-------------

[](#configuration)

`config/packages/survos_ark.yaml`:

```
survos_ark:
  naan: '12345'
  resolver_base_url: 'https://your-domain.org'
  shoulder: ''
  template: 'fk.reedeeedk'
  local_path: '/ark'
  db_type: 'sqlite'
  db_path: '%kernel.project_dir%/var/noid'
  auto_mint: true
  n2t_resolve: false
```

Making an entity ARK-enabled
----------------------------

[](#making-an-entity-ark-enabled)

Implement `ArkableInterface` and use `ArkableTrait`:

```
use Survos\ArkBundle\Contract\ArkableInterface;
use Survos\ArkBundle\Doctrine\ArkableTrait;

class Item implements ArkableInterface
{
    use ArkableTrait;

    public function getArkTarget(): string
    {
        return '/items/' . $this->id;   // relative URLs are prefixed with resolver_base_url
    }

    public function getArkObjectType(): string
    {
        return 'item';
    }
}
```

An ARK is minted automatically on first `persist()`. For entities with pre-generated IDs (ULID/UUID), an `ArkBinding` row is also created in the same flush cycle. Auto-increment entities are indexed lazily on first resolution or via `ark:reindex`.

ULID ARKs
---------

[](#ulid-arks)

`ArkUlidCodec` converts a ULID to a fixed 22-character URL-safe name and back. It uses a base58 alphabet that avoids `0`, `O`, `1`, `I`, and `l`; a true base64url alphabet cannot avoid those characters.

```
$name = $codec->name($ulid);
$ark = $codec->ark($ulid);
$url = $codec->url($ulid);
$n2tUrl = $codec->n2tUrl($ulid);
$ulid = $codec->ulid($name);
```

The existing route `GET /ark/{naan}/{name}` matches these names, so an app can decode the `{name}` segment and resolve it without storing the ARK separately. For QR codes and public links, encode the N2T URL. N2T resolves `https://n2t.net/ark:/12345/{name}` to the configured resolver URL, for example `https://your-domain.org/ark/12345/{name}`:

```
{{ ark_ulid_n2t_url(item.id) }}
```

Twig functions:

FunctionDescription`ark_ulid_name(ulid)`Deterministic 22-character name`ark_ulid_url(ulid)`Local resolver URL`ark_ulid_n2t_url(ulid)`Public N2T ARK URL for QR codes`ark_ulid(name)`Decode the name back to a canonical ULID stringResolution order
----------------

[](#resolution-order)

1. `ark_binding` table — single SQL query (primary path)
2. NOID database — in-process binary store (legacy/fallback)
3. Entity scan — iterates all `ArkableInterface` classes; lazily populates both stores on hit

ArkBinding entity
-----------------

[](#arkbinding-entity)

`ark_binding` is a pivot table that links every minted ARK to its owning entity:

ColumnDescription`ark`Full ARK string, e.g. `ark:/12345/ab12345` (unique)`entityClass`Short class name, e.g. `Item`, `Scan``entityId`Entity identifier as string`targetUrl`Cached redirect URL`label`Optional human-readable labelWith `survos/field-bundle` installed, `ArkBinding` appears in the admin navbar under the **ARK** group.

Commands
--------

[](#commands)

CommandDescription`ark:mint [count]`Mint N ARKs`ark:bind  `Manually bind a name to a URL`ark:resolve `Look up a binding`ark:validate `Check the check character`ark:bulk-mint`Mint ARKs for all unminted `ArkableInterface` entities`ark:reindex`Re-sync all NOID and `ArkBinding` entries from entity targets`ark:report`Report ARK coverage across entities`ark:export`Export `ArkBinding` rows as JSONL (stdout or `--output file.jsonl.gz`)`ark:import`Import `ArkBinding` rows from JSONL (stdin or `--input file.jsonl.gz`)Routes
------

[](#routes)

RouteDescription`GET /ark/{naan}/{name}`Resolve and redirect (301); append `?info` for ERC metadata`GET /ark/{naan}/_probe`Service health probe — always 200Persistence
-----------

[](#persistence)

LayerWhat it storesAuthoritative?`ark_binding` Doctrine tableARK → entity class, ID, target URL**Yes — back this up**NOID SQLite file (`var/noid/`)Minting sequence + URL cacheNo — rebuild with `ark:reindex`The NOID file is semi-ephemeral: if lost, run `ark:reindex` to repopulate it from the `ark_binding` table. Available backends: `sqlite` (default), `mysql`, `pdo`, `bdb` (BerkeleyDB), `lmdb`, `xml`.

ULID compression
----------------

[](#ulid-compression)

A ULID is 128 bits. Its canonical Crockford Base32 form is 26 characters; the same bytes can be represented more compactly:

FormatLengthNotesBinary16 bytesStorage (Postgres `uuid`, `BINARY(16)`)Base64url22 charsShortest sensible ASCII formBase5822 charsNo ambiguous charsBase32 (Crockford)26 charsCanonical, case-insensitive, sortableRFC 4122 UUID36 charsInterop with UUID tooling### Round-trip with `symfony/uid`

[](#round-trip-with-symfonyuid)

```
use Symfony\Component\Uid\Ulid;

$ulid  = new Ulid('01ARZ3NDEKTSV4RRFFQ69G5FAV');
$short = $ulid->toBase64();          // 22 chars, URL-safe
$back  = Ulid::fromBase64($short);   // round-trips
```

### Recommendation

[](#recommendation)

- **Database**: 16-byte binary (`uuid` column).
- **URLs / compact tokens**: Base64url, 22 chars.
- **ARK identifiers / human-transcribable IDs**: keep the 26-char Crockford Base32 — case-insensitive, no `0/O` or `1/I/l` confusion, lexicographically sortable. The 4 saved characters aren't worth losing those properties.

22 chars is the practical floor for ASCII. Base85 reaches 20 but includes characters (`"`, `'`, `\`) that are awkward in URLs and JSON.

###  Health Score

49

—

FairBetter than 94% of packages

Maintenance96

Actively maintained with recent releases

Popularity12

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity68

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

112

Last Release

25d ago

Major Versions

1.0.0 → 2.0.1262026-03-10

PHP version history (2 changes)1.0.0PHP ^8.2

2.0.206PHP ^8.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/21b39551f92ed4143772c622f9e571589c5a72c96ab3c53fe67489ce0d83e806?d=identicon)[tacman1123](/maintainers/tacman1123)

---

Top Contributors

[![tacman](https://avatars.githubusercontent.com/u/619585?v=4)](https://github.com/tacman "tacman (16 commits)")

---

Tags

symfonyidentifierresolverarkarchivalnoid

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/survos-ark-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/survos-ark-bundle/health.svg)](https://phpackages.com/packages/survos-ark-bundle)
```

###  Alternatives

[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k17.5M373](/packages/easycorp-easyadmin-bundle)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.4M196](/packages/sulu-sulu)[open-dxp/opendxp

Content &amp; Product Management Framework (CMS/PIM)

9317.2k55](/packages/open-dxp-opendxp)[chameleon-system/chameleon-base

The Chameleon System core.

1027.9k4](/packages/chameleon-system-chameleon-base)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
