artifactdock

    A lightweight MoonBit-native OCI Distribution registry server

    oci
    registry
    wasm
    container
    artifacts
    Download zip
    Author
    Version
    0.2.0
    License
    Apache-2.0
    Last updated
    14 hours ago
    Downloads
    1

    Dependencies

    #ArtifactDock

    ArtifactDock is a lightweight, self-hosted OCI Distribution registry written primarily in MoonBit. It stores container images, Wasm packages, and other OCI artifacts on a local filesystem and exposes the standard /v2/ HTTP API.

    The project is intentionally complementary to MoonBit's existing ecosystem: mizchi/oci_wasm and mizchi/wacon provide clients and runtimes, while MoonOCI builds local OCI layouts. ArtifactDock supplies the missing deployable server boundary.

    #Status

    The first release targets a reliable single-node registry suitable for local development, CI fixtures, and private Wasm distribution:

    • OCI /v2/ discovery
    • Content-addressed SHA-256 blob storage
    • Repository-scoped blob visibility with cross-repository mounts
    • Monolithic and chunked blob uploads (POST, PATCH, PUT)
    • Upload sessions bound to their repository
    • Blob HEAD/GET
    • Blob byte-range GET for resuming interrupted downloads (206/416)
    • Blob deletion with repository-safe shared content retention
    • Manifest PUT, HEAD, GET by tag or digest, with UTF-8 JSON/schema validation, descriptor checks, request/body media-type matching, and repository-local dependency checks
    • Digest-addressed manifest pushes with validated repeated tag parameters and OCI-Tag responses
    • Manifest and tag deletion with repository-safe shared blob retention
    • Manifest media-type preservation for Wasm and other OCI artifacts
    • ASCII sorted tag listing with OCI n/last pagination and next-page links
    • Docker-compatible repository catalog (/v2/_catalog) with n/last pagination
    • OCI 1.1 referrers discovery with artifactType filtering, persistent indexing, digest-ordered pagination, and next-page links
    • Restart-safe filesystem persistence and atomic writes
    • Offline garbage collection with a non-destructive dry-run mode

    Authentication, remote object storage, and replication are deliberately follow-up work. The server does not build images or execute containers.

    Blob data created before repository-scoped links were introduced must be re-pushed to its repository before the blob endpoint can serve it. In-progress uploads from that earlier storage format cannot be resumed. Repository scoping follows OCI API semantics; it is not user authentication.

    #Run

    Install MoonBit, then run:

    moon run src -- --root ./artifactdock-data --host 127.0.0.1 --port 5000

    The server listens on http://127.0.0.1:5000. A minimal push/pull smoke test is in examples/push-pull.ps1.

    #Garbage collection

    Stop the registry before running garbage collection. Preview reclaimable content:

    moon run src -- --root ./artifactdock-data --gc --dry-run

    Remove unreferenced global content while preserving repository blob links and manifests:

    moon run src -- --root ./artifactdock-data --gc

    #Development

    moon fmt moon check --target native moon test --target native moon build --target native

    The implementation follows the OCI Distribution Specification. The conformance suite is the long-term compatibility target.

    #License

    Apache-2.0. See LICENSE.

    BlobRange

    type BlobRange

    RegistryRoute

    type RegistryRoute

    RegistryStore

    type RegistryStore

    RegistryStore::append_upload

    async fn RegistryStore::append_upload(self : RegistryStore, repository : String, upload_id : String, data : Bytes) -> Bool

    RegistryStore::collect_garbage

    async fn RegistryStore::collect_garbage(self : RegistryStore, dry_run : Bool) -> (Int, Int, Int64)

    Remove global content that has no repository blob or manifest reference. Returns the number of content files scanned, reclaimable files, and bytes.

    RegistryStore::delete_manifest

    async fn RegistryStore::delete_manifest(self : RegistryStore, repository : String, reference : String) -> Bool

    Delete a tag, or remove a manifest and every tag that points to it. The content-addressed blob remains available for other repositories and GC.

    RegistryStore::delete_repository_blob

    async fn RegistryStore::delete_repository_blob(self : RegistryStore, repository : String, digest : String) -> Bool

    Remove a repository's blob link while retaining shared content for GC.

    RegistryStore::delete_upload

    async fn RegistryStore::delete_upload(self : RegistryStore, repository : String, upload_id : String) -> Bool

    Remove an in-progress upload session.

    RegistryStore::ensure

    async fn RegistryStore::ensure(self : RegistryStore) -> Unit

    RegistryStore::finalize_upload

    async fn RegistryStore::finalize_upload(self : RegistryStore, repository : String, upload_id : String, expected_digest : String) -> (Int, String?)

    RegistryStore::get_blob

    async fn RegistryStore::get_blob(self : RegistryStore, digest : String) -> Bytes?

    RegistryStore::get_manifest

    async fn RegistryStore::get_manifest(self : RegistryStore, repository : String, reference : String) -> Bytes?

    RegistryStore::get_repository_blob

    async fn RegistryStore::get_repository_blob(self : RegistryStore, repository : String, digest : String) -> Bytes?

    RegistryStore::has_blob

    async fn RegistryStore::has_blob(self : RegistryStore, digest : String) -> Bool

    RegistryStore::has_repository_blob

    async fn RegistryStore::has_repository_blob(self : RegistryStore, repository : String, digest : String) -> Bool

    RegistryStore::list_manifest_digests

    async fn RegistryStore::list_manifest_digests(self : RegistryStore, repository : String) -> Array[String]

    Return every manifest digest currently addressable in a repository.

    RegistryStore::list_referrer_descriptors

    async fn RegistryStore::list_referrer_descriptors(self : RegistryStore, repository : String, subject : String) -> Array[(String, String?, Json)]

    Return persisted referrer descriptors without reopening each manifest. Entries are sorted by digest so pagination remains stable across restarts.

    RegistryStore::list_referrer_digests

    async fn RegistryStore::list_referrer_digests(self : RegistryStore, repository : String, subject : String) -> Array[String]

    Return indexed, still-addressable referrer manifests for a subject.

    RegistryStore::list_repositories

    async fn RegistryStore::list_repositories(self : RegistryStore) -> Array[String]

    Return repositories that have persisted blobs, tags, or manifests.

    RegistryStore::list_tags

    async fn RegistryStore::list_tags(self : RegistryStore, repository : String) -> Array[String]

    RegistryStore::manifest_content_type

    async fn RegistryStore::manifest_content_type(self : RegistryStore, repository : String, digest : String) -> String

    Return a persisted manifest media type, or the OCI image default.

    RegistryStore::mount_blob

    async fn RegistryStore::mount_blob(self : RegistryStore, repository : String, source : String, digest : String) -> Bool

    RegistryStore::new

    fn RegistryStore::new(root : String) -> RegistryStore

    RegistryStore::put_blob

    async fn RegistryStore::put_blob(self : RegistryStore, data : Bytes) -> String

    RegistryStore::put_manifest

    async fn RegistryStore::put_manifest(self : RegistryStore, repository : String, reference : String, data : Bytes) -> String

    RegistryStore::put_manifest_with_type

    async fn RegistryStore::put_manifest_with_type(self : RegistryStore, repository : String, reference : String, data : Bytes, content_type : String) -> String

    Store a manifest and preserve the media type supplied by its client.

    RegistryStore::start_upload

    async fn RegistryStore::start_upload(self : RegistryStore, repository : String) -> String

    RegistryStore::tag_manifest

    async fn RegistryStore::tag_manifest(self : RegistryStore, repository : String, tag : String, digest : String) -> Bool

    Add a tag to a manifest already stored in the repository.

    RegistryStore::upload_size

    async fn RegistryStore::upload_size(self : RegistryStore, repository : String, upload_id : String) -> Int?

    Return the current byte length of an upload session.

    serve

    async fn serve(store : RegistryStore, host : String, port : Int) -> Unit

    sha256_digest

    fn sha256_digest(data : Bytes) -> String

    Compute a lowercase SHA-256 digest in OCI's sha256:<hex> form.

    valid_digest

    fn valid_digest(value : String) -> Bool

    Validate an OCI digest reference.

    valid_manifest_reference

    fn valid_manifest_reference(value : String) -> Bool

    valid_reference

    fn valid_reference(value : String) -> Bool

    Tags and upload IDs are single safe path components.

    valid_repository

    fn valid_repository(value : String) -> Bool

    This keeps filesystem mapping predictable and rejects traversal attempts.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io