Personally I've been making OCI images using Nix, just by running tar, sha256sum and jq:
- We can get a Nix derivation's dependencies using `writeDependenciesToFile`
- We can put these dependencies (alongside anything else we like) into an OCI "layer" using GNU tar: the `--hard-dereference` and `--mode=755` options work well, and we can include directories using `-C /foo --filesFrom=foo.txt` where foo.txt comes from `cd /foo && find . -maxdepth 1`, with the leading `./` removed.
- A "digest" is just a JSON file like `{size: 123, digest: "abc"}`, e.g. generated using `stat` and `sha256sum`
- Those sha256sums can be referenced in a config.json, like `{"rootfs": {"type": "layers", "diff_ids": [...]}}`; along with the application-specific config (EntryPoint, WorkingDir, etc.)
- Finally, a "manifest" is just another JSON file; with `{"mediaType": "application/vnd.oci.image.config.v1+json"}` for the config digest, and `{"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip"}` for each layer digest.
Unlike Docker, these commands can all be run directly (from the "host", although it's not really hosting anything, since we don't need any containers or VMs to run the above); their contents are completely declarative (since we're just zipping up pre-existing files, e.g. built/fetched by Nix; rather than mutating a filesystem in-place); we can do it on any OS (e.g. no need for a Linux VM on macOS); etc.