{"id":17717278,"url":"https://github.com/bwesterb/mtc","last_synced_at":"2025-05-06T20:41:47.169Z","repository":{"id":199994643,"uuid":"704600653","full_name":"bwesterb/mtc","owner":"bwesterb","description":"Go implementation of Merkle Tree Certificates","archived":false,"fork":false,"pushed_at":"2025-04-24T13:33:10.000Z","size":302,"stargazers_count":16,"open_issues_count":12,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-26T15:58:17.436Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bwesterb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-13T15:58:04.000Z","updated_at":"2025-04-24T13:32:35.000Z","dependencies_parsed_at":"2024-01-07T14:32:34.071Z","dependency_job_id":"c862d519-70ab-471b-9c4f-03abc3523338","html_url":"https://github.com/bwesterb/mtc","commit_stats":null,"previous_names":["bwesterb/mtc"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bwesterb%2Fmtc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bwesterb%2Fmtc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bwesterb%2Fmtc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bwesterb%2Fmtc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bwesterb","download_url":"https://codeload.github.com/bwesterb/mtc/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252768278,"owners_count":21801366,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-25T14:19:39.656Z","updated_at":"2025-05-06T20:41:47.143Z","avatar_url":"https://github.com/bwesterb.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"Merkle Tree Certificates for TLS\n================================\n\n🚨 Merkle Tree Certificates (**MTC**) is a moving target.\n\nImplementation of [Merkle Tree Certificates for TLS](\n    https://davidben.github.io/merkle-tree-certs/draft-davidben-tls-merkle-tree-certs.html)\n    in Go. This contains a Certification Authority (CA), Mirror, and\n    code to verify certificates.\nThis does not contain integration with TLS (yet) or the ACME bits (yet).\nAt the moment we differ from `-04` and `main` branch of the specification,\n    by including some [unmerged PRs](https://github.com/davidben/merkle-tree-certs/pulls).\n\nDemo\n----\n\nFor a proper introduction and motivation, check out the [draft specification](\n    https://davidben.github.io/merkle-tree-certs/draft-davidben-tls-merkle-tree-certs.html)\nand [David's TLS working group presentation at IETF116](\n    https://youtu.be/u_sFyz4F7dc?si=inG4bgBwKLzrBuvY\u0026t=2566).\n\nMerkle Tree Certificates is an **optimisation** to the WebPKI (including\n[Certificate Transparency](https://certificate.transparency.dev))\nmotivated by the [large sizes](\n    https://dadrian.io/blog/posts/pqc-signatures-2024/) of\n[typical post-quantum signatures and public keys](\n    https://blog.cloudflare.com/another-look-at-pq-signatures/),\nto reduce the number of keys and signatures required for the common case where\n\n 1. Certificate issuance does not have to be immediate. For instance, because\n    a certificate can be requested ahead of time for an existing domain\n    by an [ACME client](https://acmeclients.com)\n    like [certbot](https://certbot.eff.org).\n\n 2. The relying party (eg. browser) has a trusted update mechanism.\n    There are also several ways to use MTC without trusted update mechanism,\n    with various trade-offs: see the [Relying Party Policy](\n    https://davidben.github.io/merkle-tree-certs/draft-davidben-tls-merkle-tree-certs.html#name-relying-party-policy)\n    section of the specification.\n\nIf we're not in this case (which is estimated to be\n[less than 0.1%](https://www.youtube.com/watch?v=f8unMB2Qjho) of the time),\nthen we fall back to regular X.509 certificates.\n\n### Intermezzo: `mtc` commandline tool\n\nTo play around with MTC, you can install the `mtc` commandline tool:\n\n```\n$ go install github.com/bwesterb/mtc/cmd/mtc@v0.1.2\n```\n\n### Assertions\n\nIn MTC CAs certify **assertions**, which bind a **subject** to a **claim**.\nAn informal example of an assertion is:\n\n\u003e For TLS, you can trust the P-256 public key `a02342ff2…23ef`\n\u003e when visiting `example.com` or `198.51.100.60`.\n\nThe first part (TLS and the public key) is the *subject*, and the\nlatter (domain and IP) are the *claim*.\nRoughly, an assertion is like a certificate without the signature.\n\nYou can create a request for an assertion to be signed with the\n`mtc new-assertion-request` command. First, let's quickly create\na P-256 public key to play with.\n\n```\n$ openssl ecparam -name prime256v1 -genkey -out p256.priv\n$ openssl ec -in p256.priv -pubout -out p256.pub\n```\n\nNow we create an assertion that this P-256 public key should\nbe valid for `example.com` and `198.51.100.60`, and write it to\nthe `my-asr`.\n\n```\n$ mtc new-assertion-request --tls-pem p256.pub --dns example.com --ip4 198.51.100.60 -o my-asr\n```\n\nLet's check it using `mtc inspect`:\n\n```\n$ mtc inspect assertion-request my-asr\nchecksum         2024bdbffe399acca37d299a03c047aa33ef596ae471c17698a0566d00951bd9\nnot_after        unset\nsubject_type     TLS\nsignature_scheme p256\npublic_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a\ndns              [example.com]\nip4              [198.51.100.60]\nevidence-list (0 entries)\n```\n\nAn assertion request can contain two bits of extra information besides\nthe assertion itself. First is a `not_after` field that limits\nthe validity of the assertion when published.\nThe second is optional \"evidence\" that's published alongside the\nassertions. In the future this could for instance be used for\nserialized DNSSEC proofs.\n\nWe can also create an assertion request derived from an existing X.509\ncertificate at a TLS server using the `-X` flag:\n\n```\n$ mtc new-assertion-request -X example.com:443 | mtc inspect assertion-request\nchecksum         015d4da06412b4e48f8d93bcbe7bbf43c4684579322cbfbc88d8b653bb2f7e51\nnot_after        unset\nsubject_type     TLS\nsignature_scheme p256\npublic_key_hash  8d566a5407ab85b413925911c4ce6b13013516006fa8568bf2ec58b9abe04af1\ndns              [example.com]\ndns_wildcard     [example.com]\nevidence-list (1 entries)\numbilical\n certificate 0\n  subject    CN=*.example.com,O=Internet Corporation for Assigned Names and Numbers,L=Los Angeles,ST=California,C=US\n  issuer     CN=DigiCert Global G3 TLS ECC SHA384 2020 CA1,O=DigiCert Inc,C=US\n  serial_no  ad893bafa68b0b7fb7a404f06ecaf9a\n  not_before 2025-01-15 00:00:00 +0000 UTC\n  not_after  2026-01-15 23:59:59 +0000 UTC\n certificate 1\n  subject    CN=DigiCert Global G3 TLS ECC SHA384 2020 CA1,O=DigiCert Inc,C=US\n  issuer     CN=DigiCert Global Root G3,OU=www.digicert.com,O=DigiCert Inc,C=US\n  serial_no  b00e92d4d6d731fca3059c7cb1e1886\n  not_before 2021-04-14 00:00:00 +0000 UTC\n  not_after  2031-04-13 23:59:59 +0000 UTC\n```\n\n### Batches, merkle trees and signed validity windows\n\nAn MTCA doesn't give you a certificate for an assertion request immediately.\nInstead, assertions are queued and issued in **batches** with a fixed rhythm,\nfor instance a batch is issued once every hour.\nAll assertions in a single batch by default are valid for the same period of\ntime, the **validity window**, which is, for instance, two weeks.\nThe CA publishes these batches publicly over HTTP.\n\nFor each batch, the CA computes a [Merkle tree](\n    https://en.wikipedia.org/wiki/Merkle_tree).\nThis condenses all the assertions in that batch into a single **tree head** hash.\nFor every batch, the CA signs that tree head together with all the tree heads\nof the currently valid batches. This signature, together with those\nsigned tree heads is called the **signed validity window** for that batch,\nwhich is published alongside the assertions.\n\n### Creating a CA\n\nLet's create an MTC CA.\n\n```\n$ mtc ca new --batch-duration 5m --lifetime 1h 62253.12.15 ca.example.com/path\n```\n\nThis creates a new MTC CA in the current working directory. It's configured\nto issue a batch every 5 minutes, and for each batch to be valid for an hour.\nFor a real CA we'd want batch durations in the order of an hour,\nand a lifetime of a week or two. In this demo we shorten things a bit, so\nwe don't have to wait too long.\n\nThe CA is configured to be hosted at `https://ca.example.com/path` and\nto be identified by the [trust anchor identifier](\n    https://datatracker.ietf.org/doc/draft-ietf-tls-trust-anchor-ids/) 62253.12.15.\nYou can get your own by requesting a [private enterprise number here](\n    https://www.iana.org/assignments/enterprise-numbers/).\n\nLet's have a look at the files created:\n\n```\n$ find .\n.\n./signing.key\n./www\n./www/mtc\n./www/mtc/v04b\n./www/mtc/v04b/ca-params\n./www/mtc/v04b/batches\n./queue\n./tmp\n```\n\nThe `signing.key` file contains the private key of the keypair used by the CA.\n\nThe `www` folder contains the files that have to be served\nat `https://ca.example.com/path`. At the moment, the only file of interest\nis `ca-params`, which contains the information about the CA:\n\n```\n$ mtc inspect ca-params www/mtc/v04b/ca-params\nissuer                 62253.12.15\nstart_time             1745420554 2025-04-23 15:02:34 +0000 UTC\nbatch_duration         300        5m0s\nlife_time              3600       1h0m0s\nstorage_window_size    24         2h0m0s\nvalidity_window_size   12\nserver_prefix          ca.example.com/path\npublic_key fingerprint ml-dsa-87:84489bcb42b411a85d163ae95e7deb92b106a75840819a985e44d0e01ae3238e\n```\n\nThe `batches` folder is empty, because there are no batches issued yet.\n\nThe `queue` file contains the assertion requests that will be fulfilled\nduring the next issuance.\n\n### Issuing our first batch\n\nLet's issue our first assertion. We can read the assertion request from disk we've\ncreated earlier with `mtc new-assertion-request`:\n\n```\n$ mtc ca queue -i my-asr \n$ mtc ca show-queue\nnot_after        2025-04-23 16:02:33 +0000 UTC\nsubject_type     TLS\nsignature_scheme p256\npublic_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a\ndns              [example.com]\nip4              [198.51.100.60]\nevidence-list (0 entries)\n\nTotal number of assertion requests in queue: 1\n```\n\nWe can also queue an assertion request ad hoc:\n\n```\n$ mtc ca queue --tls-pem p256.pub -d other.example.com -d second.example.com\n$ mtc ca show-queue | tail -n 10\nnot_after        2025-04-23 16:02:33 +0000 UTC\nsubject_type     TLS\nsignature_scheme p256\npublic_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a\ndns              [example.com]\nip4              [198.51.100.60]\nevidence-list (0 entries)\n\nTotal number of assertion requests in queue: 2\n```\n\nLet's issue our first batch.\n\n```\n$ mtc ca issue\n2025/04/23 17:05:20 INFO Starting issuance time=2025-04-23T15:05:20.664Z\n2025/04/23 17:05:20 INFO Current state expectedStored=0 expectedActive=0 existingBatches=⌀\n2025/04/23 17:05:20 INFO To issue batches=0\n```\n\nAnd let's check:\n\n```\n$ find .\n.\n./signing.key\n./www\n./www/mtc\n./www/mtc/v04b\n./www/mtc/v04b/ca-params\n./www/mtc/v04b/batches\n./www/mtc/v04b/batches/0\n./www/mtc/v04b/batches/0/validity-window\n./www/mtc/v04b/batches/0/tree\n./www/mtc/v04b/batches/0/entries\n./www/mtc/v04b/batches/0/evidence\n./www/mtc/v04b/batches/0/index\n./www/mtc/v04b/batches/latest\n./queue\n./tmp\n```\n\nWe see batch `0` has been created. `latest` is a symlink to to `0`.\n\nNow, let's have a look at the batch. The `entries` file is essentially\nthe list of assertions: the difference between a regular assertion\nand an entry is that with an entry, the public key has been replaced\nby the hash of the public key.\n\n```\n$ mtc inspect entries www/mtc/v04b/batches/0/entries\nkey              0b65c8a5f69e88fd1eb58dff4d317f6173bd31773e14d99ace88a2aa7062fdd9\nnot_after        2025-04-23 16:02:33 +0000 UTC\nsubject_type     TLS\nsignature_scheme p256\npublic_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a\ndns              [other.example.com second.example.com]\n\nkey              78b5ccc905b693659bf6581011f8efb17fd7aedf9ca70a196a22923f560feeca\nnot_after        2025-04-23 16:02:33 +0000 UTC\nsubject_type     TLS\nsignature_scheme p256\npublic_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a\ndns              [example.com]\nip4              [198.51.100.60]\n\nTotal number of entries: 2\n```\n\nThe `validity-window` is the signed validity window: the tree heads of\nthe currently valid batches:\n\n```\n$ mtc inspect -ca-params www/mtc/v04b/ca-params validity-window www/mtc/v04b/batches/0/validity-window               \nsignature       ✅\nbatch_number    0\ntree_heads[0]   043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345\ntree_heads[-1]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-2]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-3]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-4]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-5]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-6]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-7]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-8]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-9]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-10] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-11] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\n```\n\nWe need to pass the `ca-params` file to be able to parse the file, and\ncheck the signature therein. (As not all previous batches exist, they use\nthe placeholder value for an empty tree.)\n\nThe `tree` file contains the Merkle tree.\n\n```\n$ mtc inspect tree www/mtc/v04b/batches/0/tree \nnumber of leaves 2\nnumber of nodes  3\ntree head        043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345\n```\n\nThe `evidence` file contains the optional evidence that can be provided\nwith the assertion request. We did not pass any, so they're empty:\n\n```\n$ mtc inspect evidence www/mtc/v04b/batches/0/evidence\nevidence-list (0 entries)\n\nevidence-list (0 entries)\n\nTotal number of evidence lists: 2\n```\n\nFinally, the `index` file allows a quick lookup in `entries` (and `evidence`)\nby key (hash of the assertion):\n\n```\n$ mtc inspect index www/mtc/v04b/batches/0/index\n                                                             key   seqno  offset\n0b65c8a5f69e88fd1eb58dff4d317f6173bd31773e14d99ace88a2aa7062fdd9       0       0       0\n78b5ccc905b693659bf6581011f8efb17fd7aedf9ca70a196a22923f560feeca       1      91       3\n\ntotal number of entries: 2\n```\n\n### Issuing more batches\n\nAs we just issued a new batch, we need to wait a while before the\nnext batch is ready to issue.\n\nLet's queue some more assertions, wait a bit, and issue a new batch.\n\n```\n$ mtc ca queue --tls-pem p256.pub -d 1.example.com\n$ mtc ca queue --tls-pem p256.pub -d 2.example.com\n$ mtc ca queue --tls-pem p256.pub -d 3.example.com\n$ mtc ca issue\n2025/04/23 17:12:45 INFO Starting issuance time=2025-04-23T15:12:45.869Z\n2025/04/23 17:12:45 INFO Current state expectedStored=0,…,2 expectedActive=0,…,2 existingBatches=0\n2025/04/23 17:12:45 INFO To issue batches=1,2\n$ find .\n.\n./signing.key\n./www\n./www/mtc\n./www/mtc/v04b\n./www/mtc/v04b/ca-params\n./www/mtc/v04b/batches\n./www/mtc/v04b/batches/0\n./www/mtc/v04b/batches/0/validity-window\n./www/mtc/v04b/batches/0/tree\n./www/mtc/v04b/batches/0/entries\n./www/mtc/v04b/batches/0/evidence\n./www/mtc/v04b/batches/0/latest\n./www/mtc/v04b/batches/0/index\n./www/mtc/v04b/batches/latest\n./www/mtc/v04b/batches/1\n./www/mtc/v04b/batches/1/validity-window\n./www/mtc/v04b/batches/1/tree\n./www/mtc/v04b/batches/1/entries\n./www/mtc/v04b/batches/1/evidence\n./www/mtc/v04b/batches/1/index\n./www/mtc/v04b/batches/2\n./www/mtc/v04b/batches/2/validity-window\n./www/mtc/v04b/batches/2/tree\n./www/mtc/v04b/batches/2/entries\n./www/mtc/v04b/batches/2/evidence\n./www/mtc/v04b/batches/2/index\n./queue\n./tmp\n```\n\nAs we waited a bit longer, the current batch is `2`, which will contain\nthe queued assertions. The batch `1` in between will be empty.\nNow `latest` points to `2`, and its signed validity window is more interesting.\n\n```\n$ mtc inspect -ca-params www/mtc/v04b/ca-params validity-window www/mtc/v04b/batches/1/validity-window\nsignature      ✅\nbatch_number   2\ntree_heads[2]  03a95ba3c354e2b0eb4bea9b111dbc8b97e2c90b85ddcc63d4b635b16f77005d\ntree_heads[1]  7ceda88ec6c8e34ecacde47588e2605fb86192b94ca96cb897fa6ff442198c8c\ntree_heads[0]  043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345\ntree_heads[-1] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-2] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-3] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-4] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-5] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-6] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-7] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-8] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\ntree_heads[-9] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf\n```\n\n### Creating a certificate\n\nIn MTC, a **certificate** is an assertion together with a trust anchor identifier\n(to identify the CA), and an authentication path in the Merkle tree.\nLet's create one for our initial assertion.\n\n```\n$ mtc ca cert -i my-asr -o my-cert\n```\n\nIf we inspect the certificate, it can recompute the root from the\nauthentication path and CA parameters:\n\n```\n$ mtc inspect -ca-params www/mtc/v04b/ca-params cert my-cert\nsubject_type     TLS\nsignature_scheme p256\npublic_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a\ndns              [example.com]\nip4              [198.51.100.60]\n\nproof_type           merkle_tree_sha256\nCA TAI               62253.12.15\nBatch number         0\nindex                1\nrecomputed tree head 043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345\nauthentication path\n 8964f010faa9e499b21917f8792b541b7b1ac19f313a5d53094c698c2edc330b\n```\n\nThis is indeed the root of batch `0`, and so this certificate is valid.\n\n### Verify certificate\n\nTo automate this, there is the `mtc verify` command that takes\na certificate, the CA parameters, and a signed validity window.\n\n```\n$ mtc verify -ca-params www/mtc/v04b/ca-params -validity-window www/mtc/v04b/batches/1/validity-window my-cert\n$ echo $?\n0\n```\n\nStatus code 0 means verification succeeded.\n\nFor transparency, you should not get the signed validity window directly\nfrom the CA, but rather from one or more mirrors (see below).\n\n### Run CA as server\n\nAn Merkle Tree CA can be run just from the commandline, but it's often\nmore convenient to run it as a server. To start the server, run:\n\n```\n$ mtc ca serve -listen-addr localhost:8080\n```\n\nThis will accept HTTP requests on `localhost:8080` and serve the static\nfiles. It will also accept queue requests; periodically issue new batches;\nand return issued certificates.\n\nGet and inspect CA parameters.\n\n```\n$ curl -s \"http://localhost:8080/mtc/v04b/ca-params\" -o ca-params\n$ mtc inspect ca-params ca-params\nissuer                 62253.12.15\nstart_time             1745420554 2025-04-23 15:02:34 +0000 UTC\nbatch_duration         300        5m0s\nlife_time              3600       1h0m0s\nstorage_window_size    24         2h0m0s\nvalidity_window_size   12\nserver_prefix          ca.example.com/path\npublic_key fingerprint ml-dsa-87:84489bcb42b411a85d163ae95e7deb92b106a75840819a985e44d0e01ae3238e\n```\n\nQueue up the assertion created in above.\n\n```\n$ curl -X POST \"http://localhost:8080/ca/queue\" --data-binary \"@my-asr\" -w \"%{http_code}\"\n200\n```\n\nAfter it's been issued, we can get the certificate via the `/ca/cert` endpoint:\n\n```\n$ curl -X POST \"http://localhost:8080/ca/cert\" --data-binary \"@my-asr\" -o my-cert\n$ mtc inspect -ca-params ca-params cert my-cert\nsubject_type     TLS\nsignature_scheme p256\npublic_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a\ndns              [example.com]\nip4              [198.51.100.60]\n\nproof_type           merkle_tree_sha256\nCA TAI               62253.12.15\nBatch number         0\nindex                1\nrecomputed tree head 043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345\nauthentication path\n 8964f010faa9e499b21917f8792b541b7b1ac19f313a5d53094c698c2edc330b\n```\n\n### Mirroring a CA\n\nWe can set up a new mirror with the `mtc mirror new` command:\n\n```\n$ mtc mirror new ca.example.com/path\n```\n\nThis will download the `ca-params`\nfrom `https://ca.example.com/path/mtc/v04b/ca-params` and\nset up a directory structure similar to that of a CA:\n\n```\n$ find .\n.\n./www\n./www/mtc\n./www/mtc/v04b\n./www/mtc/v04b/ca-params\n./www/mtc/v04b/batches\n./tmp\n```\n\nTo bring the mirror up to date with the CA, use the `update` command:\n\n```\n$ mtc mirror update\n2025/04/24 11:54:53 INFO Current state expectedStoredRemote=0 expectedActiveRemote=0 latestRemoteBatch=0 mirroredBatches=⌀\n2025/04/24 11:54:53 INFO Fetching batch=0\n2025/04/24 11:54:53 INFO Next batch at the earliest in 49s\n$ find .\n.\n./www\n./www/mtc\n./www/mtc/v04b\n./www/mtc/v04b/ca-params\n./www/mtc/v04b/batches\n./www/mtc/v04b/batches/0\n./www/mtc/v04b/batches/0/validity-window\n./www/mtc/v04b/batches/0/tree\n./www/mtc/v04b/batches/0/entries\n./www/mtc/v04b/batches/0/evidence\n./www/mtc/v04b/batches/latest\n./tmp\n```\n\n#### Local testing\n\nTo make local testing convenient, when you use `localhost` as server prefix,\nthe mirror will use `http` instead of `https`. This allows a quick testing\nset up as follows:\n\n```\n# Set up a CA in the ca folder\n$ mtc ca -p ca new --batch-duration 5m --lifetime 1h 62253.12.15 localhost:8080\n$ mtc ca -p ca queue -X example.com:443\n$ mtc ca -p ca issue\n$ mtc ca -p ca server -listen-addr localhost:8080 \u0026\n# Set up a mirror of the CA in the mirror folder\n$ mtc mirror -p mirror new localhost:8080\n$ mtc mirror -p mirror update\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbwesterb%2Fmtc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbwesterb%2Fmtc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbwesterb%2Fmtc/lists"}