├── .github ├── dependabot.yml ├── settings.yml └── workflows │ └── render-specs.yml ├── .gitignore ├── .gitpod.yml ├── LICENSE ├── MAINTAINERS.md ├── assets ├── compiled │ ├── body.js │ ├── head.css │ ├── head.js │ └── refs.json ├── css │ ├── chart.css │ ├── custom-elements.css │ ├── index.css │ └── prism.css ├── icons.svg └── js │ ├── chart.js │ ├── custom-elements.js │ ├── font-awesome.js │ ├── index.js │ ├── markdown-it.js │ ├── mermaid.js │ ├── popper.js │ ├── prism.js │ ├── tippy.js │ └── utils.js ├── custom-assets ├── custom-body.js ├── custom-head.js └── custom.css ├── gulpfile.js ├── hyperledger-logo.svg ├── index.js ├── package-lock.json ├── package.json ├── readme.md ├── spec ├── 0_header.md ├── 10_original_did_operations.md ├── 11_besu_did_operations.md ├── 12_did_indy_did_component_syntax.md ├── 13_json_or_json-ld.md ├── 14_privacy_considerations.md ├── 15_security_considerations.md ├── 16_future_directions.md ├── 1_about.md ├── 2_abstract.md ├── 3_indy_ledger_object_glossary.md ├── 4_differences_from_did_sov.md ├── 5_target_systems.md ├── 6_motivation_and_assumptions.md ├── 7_indy_did_method_identifiers.md ├── 8_other_indy_ledger_object_identifiers.md └── 9_finding_indy_ledgers.md ├── specs.json └── src ├── asset-map.json └── markdown-it-extensions.js /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # For details on how this file works refer to: 2 | # - https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 3 | version: 2 4 | updates: 5 | # Maintain dependencies for GitHub Actions 6 | # - Check for updates once a week 7 | # - Group all updates into a single PR 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | groups: 13 | all-actions: 14 | patterns: [ "*" ] 15 | 16 | # Maintain dependencies for TypeScript and JavaScript 17 | - package-ecosystem: "npm" 18 | directory: "/" 19 | schedule: 20 | interval: "weekly" 21 | day: "monday" 22 | time: "04:00" 23 | timezone: "Canada/Pacific" 24 | ignore: 25 | - dependency-name: "*" 26 | update-types: ["version-update:semver-major", "version-update:semver-patch"] 27 | 28 | -------------------------------------------------------------------------------- /.github/settings.yml: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | 5 | repository: 6 | name: indy-did-method 7 | description: Hyperledger Indy 8 | homepage: https://wiki.hyperledger.org/display/indy 9 | default_branch: default 10 | has_downloads: true 11 | has_issues: false 12 | has_projects: false 13 | has_wiki: false 14 | archived: false 15 | private: false 16 | allow_squash_merge: true 17 | allow_merge_commit: false 18 | allow_rebase_merge: true 19 | -------------------------------------------------------------------------------- /.github/workflows/render-specs.yml: -------------------------------------------------------------------------------- 1 | name: Build Spec 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | build-and-deploy-spec: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write 14 | steps: 15 | - name: Checkout 🛎️ 16 | uses: actions/checkout@v4 # If you're using actions/checkout@v4 you must set persist-credentials to false in most cases for the deployment to work correctly. 17 | with: 18 | persist-credentials: false 19 | 20 | - name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built. 21 | run: | 22 | npm install 23 | node -e "require('spec-up')({ nowatch: true })" 24 | rm -rf .gitignore 25 | rm -rf node_modules 26 | 27 | - name: Deploy 28 | uses: peaceiris/actions-gh-pages@v4.0.0 29 | with: 30 | github_token: ${{ secrets.GITHUB_TOKEN }} 31 | publish_dir: ./ 32 | allow_empty_commit: true 33 | force_orphan: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | index.html 3 | fonts 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | .vscode/* 13 | 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # TypeScript v1 declaration files 50 | typings/ 51 | 52 | # TypeScript cache 53 | *.tsbuildinfo 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Microbundle cache 62 | .rpt2_cache/ 63 | .rts2_cache_cjs/ 64 | .rts2_cache_es/ 65 | .rts2_cache_umd/ 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | 76 | # dotenv environment variables file 77 | .env 78 | .env.test 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # Next.js build output 84 | .next 85 | 86 | # Nuxt.js build / generate output 87 | .nuxt 88 | dist 89 | 90 | # Gatsby files 91 | .cache/ 92 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 93 | # https://nextjs.org/blog/next-9-1#public-directory-support 94 | # public 95 | 96 | # vuepress build output 97 | .vuepress/dist 98 | 99 | # Serverless directories 100 | .serverless/ 101 | 102 | # FuseBox cache 103 | .fusebox/ 104 | 105 | # DynamoDB Local files 106 | .dynamodb/ 107 | 108 | # TernJS port file 109 | .tern-port 110 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | github: 2 | prebuilds: 3 | # enable for the default branch (defaults to true) 4 | main: true 5 | # enable for all branches in this repo (defaults to false) 6 | branches: true 7 | # enable for pull requests coming from this repo (defaults to true) 8 | pullRequests: true 9 | # enable for pull requests coming from forks (defaults to false) 10 | pullRequestsFromForks: true 11 | # add a check to pull requests (defaults to true) 12 | addCheck: true 13 | # add a "Review in Gitpod" button as a comment to pull requests (defaults to false) 14 | addComment: false 15 | # add a "Review in Gitpod" button to the pull request's description (defaults to false) 16 | addBadge: true 17 | 18 | tasks: 19 | - init: npm install 20 | command: | 21 | gp open readme.md . 22 | npm run edit 23 | 24 | - name: Dev Server 25 | command: npx browser-sync start --server "docs" --files "docs" --no-ui 26 | 27 | ports: 28 | - port: 3000 29 | onOpen: open-preview 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution 4.0 International Public License 58 | 59 | By exercising the Licensed Rights (defined below), You accept and agree 60 | to be bound by the terms and conditions of this Creative Commons 61 | Attribution 4.0 International Public License ("Public License"). To the 62 | extent this Public License may be interpreted as a contract, You are 63 | granted the Licensed Rights in consideration of Your acceptance of 64 | these terms and conditions, and the Licensor grants You such rights in 65 | consideration of benefits the Licensor receives from making the 66 | Licensed Material available under these terms and conditions. 67 | 68 | 69 | Section 1 -- Definitions. 70 | 71 | a. Adapted Material means material subject to Copyright and Similar 72 | Rights that is derived from or based upon the Licensed Material 73 | and in which the Licensed Material is translated, altered, 74 | arranged, transformed, or otherwise modified in a manner requiring 75 | permission under the Copyright and Similar Rights held by the 76 | Licensor. For purposes of this Public License, where the Licensed 77 | Material is a musical work, performance, or sound recording, 78 | Adapted Material is always produced where the Licensed Material is 79 | synched in timed relation with a moving image. 80 | 81 | b. Adapter's License means the license You apply to Your Copyright 82 | and Similar Rights in Your contributions to Adapted Material in 83 | accordance with the terms and conditions of this Public License. 84 | 85 | c. Copyright and Similar Rights means copyright and/or similar rights 86 | closely related to copyright including, without limitation, 87 | performance, broadcast, sound recording, and Sui Generis Database 88 | Rights, without regard to how the rights are labeled or 89 | categorized. For purposes of this Public License, the rights 90 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 91 | Rights. 92 | 93 | d. Effective Technological Measures means those measures that, in the 94 | absence of proper authority, may not be circumvented under laws 95 | fulfilling obligations under Article 11 of the WIPO Copyright 96 | Treaty adopted on December 20, 1996, and/or similar international 97 | agreements. 98 | 99 | e. Exceptions and Limitations means fair use, fair dealing, and/or 100 | any other exception or limitation to Copyright and Similar Rights 101 | that applies to Your use of the Licensed Material. 102 | 103 | f. Licensed Material means the artistic or literary work, database, 104 | or other material to which the Licensor applied this Public 105 | License. 106 | 107 | g. Licensed Rights means the rights granted to You subject to the 108 | terms and conditions of this Public License, which are limited to 109 | all Copyright and Similar Rights that apply to Your use of the 110 | Licensed Material and that the Licensor has authority to license. 111 | 112 | h. Licensor means the individual(s) or entity(ies) granting rights 113 | under this Public License. 114 | 115 | i. Share means to provide material to the public by any means or 116 | process that requires permission under the Licensed Rights, such 117 | as reproduction, public display, public performance, distribution, 118 | dissemination, communication, or importation, and to make material 119 | available to the public including in ways that members of the 120 | public may access the material from a place and at a time 121 | individually chosen by them. 122 | 123 | j. Sui Generis Database Rights means rights other than copyright 124 | resulting from Directive 96/9/EC of the European Parliament and of 125 | the Council of 11 March 1996 on the legal protection of databases, 126 | as amended and/or succeeded, as well as other essentially 127 | equivalent rights anywhere in the world. 128 | 129 | k. You means the individual or entity exercising the Licensed Rights 130 | under this Public License. Your has a corresponding meaning. 131 | 132 | 133 | Section 2 -- Scope. 134 | 135 | a. License grant. 136 | 137 | 1. Subject to the terms and conditions of this Public License, 138 | the Licensor hereby grants You a worldwide, royalty-free, 139 | non-sublicensable, non-exclusive, irrevocable license to 140 | exercise the Licensed Rights in the Licensed Material to: 141 | 142 | a. reproduce and Share the Licensed Material, in whole or 143 | in part; and 144 | 145 | b. produce, reproduce, and Share Adapted Material. 146 | 147 | 2. Exceptions and Limitations. For the avoidance of doubt, where 148 | Exceptions and Limitations apply to Your use, this Public 149 | License does not apply, and You do not need to comply with 150 | its terms and conditions. 151 | 152 | 3. Term. The term of this Public License is specified in Section 153 | 6(a). 154 | 155 | 4. Media and formats; technical modifications allowed. The 156 | Licensor authorizes You to exercise the Licensed Rights in 157 | all media and formats whether now known or hereafter created, 158 | and to make technical modifications necessary to do so. The 159 | Licensor waives and/or agrees not to assert any right or 160 | authority to forbid You from making technical modifications 161 | necessary to exercise the Licensed Rights, including 162 | technical modifications necessary to circumvent Effective 163 | Technological Measures. For purposes of this Public License, 164 | simply making modifications authorized by this Section 2(a) 165 | (4) never produces Adapted Material. 166 | 167 | 5. Downstream recipients. 168 | 169 | a. Offer from the Licensor -- Licensed Material. Every 170 | recipient of the Licensed Material automatically 171 | receives an offer from the Licensor to exercise the 172 | Licensed Rights under the terms and conditions of this 173 | Public License. 174 | 175 | b. No downstream restrictions. You may not offer or impose 176 | any additional or different terms or conditions on, or 177 | apply any Effective Technological Measures to, the 178 | Licensed Material if doing so restricts exercise of the 179 | Licensed Rights by any recipient of the Licensed 180 | Material. 181 | 182 | 6. No endorsement. Nothing in this Public License constitutes or 183 | may be construed as permission to assert or imply that You 184 | are, or that Your use of the Licensed Material is, connected 185 | with, or sponsored, endorsed, or granted official status by, 186 | the Licensor or others designated to receive attribution as 187 | provided in Section 3(a)(1)(A)(i). 188 | 189 | b. Other rights. 190 | 191 | 1. Moral rights, such as the right of integrity, are not 192 | licensed under this Public License, nor are publicity, 193 | privacy, and/or other similar personality rights; however, to 194 | the extent possible, the Licensor waives and/or agrees not to 195 | assert any such rights held by the Licensor to the limited 196 | extent necessary to allow You to exercise the Licensed 197 | Rights, but not otherwise. 198 | 199 | 2. Patent and trademark rights are not licensed under this 200 | Public License. 201 | 202 | 3. To the extent possible, the Licensor waives any right to 203 | collect royalties from You for the exercise of the Licensed 204 | Rights, whether directly or through a collecting society 205 | under any voluntary or waivable statutory or compulsory 206 | licensing scheme. In all other cases the Licensor expressly 207 | reserves any right to collect such royalties. 208 | 209 | 210 | Section 3 -- License Conditions. 211 | 212 | Your exercise of the Licensed Rights is expressly made subject to the 213 | following conditions. 214 | 215 | a. Attribution. 216 | 217 | 1. If You Share the Licensed Material (including in modified 218 | form), You must: 219 | 220 | a. retain the following if it is supplied by the Licensor 221 | with the Licensed Material: 222 | 223 | i. identification of the creator(s) of the Licensed 224 | Material and any others designated to receive 225 | attribution, in any reasonable manner requested by 226 | the Licensor (including by pseudonym if 227 | designated); 228 | 229 | ii. a copyright notice; 230 | 231 | iii. a notice that refers to this Public License; 232 | 233 | iv. a notice that refers to the disclaimer of 234 | warranties; 235 | 236 | v. a URI or hyperlink to the Licensed Material to the 237 | extent reasonably practicable; 238 | 239 | b. indicate if You modified the Licensed Material and 240 | retain an indication of any previous modifications; and 241 | 242 | c. indicate the Licensed Material is licensed under this 243 | Public License, and include the text of, or the URI or 244 | hyperlink to, this Public License. 245 | 246 | 2. You may satisfy the conditions in Section 3(a)(1) in any 247 | reasonable manner based on the medium, means, and context in 248 | which You Share the Licensed Material. For example, it may be 249 | reasonable to satisfy the conditions by providing a URI or 250 | hyperlink to a resource that includes the required 251 | information. 252 | 253 | 3. If requested by the Licensor, You must remove any of the 254 | information required by Section 3(a)(1)(A) to the extent 255 | reasonably practicable. 256 | 257 | 4. If You Share Adapted Material You produce, the Adapter's 258 | License You apply must not prevent recipients of the Adapted 259 | Material from complying with this Public License. 260 | 261 | 262 | Section 4 -- Sui Generis Database Rights. 263 | 264 | Where the Licensed Rights include Sui Generis Database Rights that 265 | apply to Your use of the Licensed Material: 266 | 267 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 268 | to extract, reuse, reproduce, and Share all or a substantial 269 | portion of the contents of the database; 270 | 271 | b. if You include all or a substantial portion of the database 272 | contents in a database in which You have Sui Generis Database 273 | Rights, then the database in which You have Sui Generis Database 274 | Rights (but not its individual contents) is Adapted Material; and 275 | 276 | c. You must comply with the conditions in Section 3(a) if You Share 277 | all or a substantial portion of the contents of the database. 278 | 279 | For the avoidance of doubt, this Section 4 supplements and does not 280 | replace Your obligations under this Public License where the Licensed 281 | Rights include other Copyright and Similar Rights. 282 | 283 | 284 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 285 | 286 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 287 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 288 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 289 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 290 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 291 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 292 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 293 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 294 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 295 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 296 | 297 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 298 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 299 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 300 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 301 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 302 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 303 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 304 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 305 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 306 | 307 | c. The disclaimer of warranties and limitation of liability provided 308 | above shall be interpreted in a manner that, to the extent 309 | possible, most closely approximates an absolute disclaimer and 310 | waiver of all liability. 311 | 312 | 313 | Section 6 -- Term and Termination. 314 | 315 | a. This Public License applies for the term of the Copyright and 316 | Similar Rights licensed here. However, if You fail to comply with 317 | this Public License, then Your rights under this Public License 318 | terminate automatically. 319 | 320 | b. Where Your right to use the Licensed Material has terminated under 321 | Section 6(a), it reinstates: 322 | 323 | 1. automatically as of the date the violation is cured, provided 324 | it is cured within 30 days of Your discovery of the 325 | violation; or 326 | 327 | 2. upon express reinstatement by the Licensor. 328 | 329 | For the avoidance of doubt, this Section 6(b) does not affect any 330 | right the Licensor may have to seek remedies for Your violations 331 | of this Public License. 332 | 333 | c. For the avoidance of doubt, the Licensor may also offer the 334 | Licensed Material under separate terms or conditions or stop 335 | distributing the Licensed Material at any time; however, doing so 336 | will not terminate this Public License. 337 | 338 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 339 | License. 340 | 341 | 342 | Section 7 -- Other Terms and Conditions. 343 | 344 | a. The Licensor shall not be bound by any additional or different 345 | terms or conditions communicated by You unless expressly agreed. 346 | 347 | b. Any arrangements, understandings, or agreements regarding the 348 | Licensed Material not stated herein are separate from and 349 | independent of the terms and conditions of this Public License. 350 | 351 | 352 | Section 8 -- Interpretation. 353 | 354 | a. For the avoidance of doubt, this Public License does not, and 355 | shall not be interpreted to, reduce, limit, restrict, or impose 356 | conditions on any use of the Licensed Material that could lawfully 357 | be made without permission under this Public License. 358 | 359 | b. To the extent possible, if any provision of this Public License is 360 | deemed unenforceable, it shall be automatically reformed to the 361 | minimum extent necessary to make it enforceable. If the provision 362 | cannot be reformed, it shall be severed from this Public License 363 | without affecting the enforceability of the remaining terms and 364 | conditions. 365 | 366 | c. No term or condition of this Public License will be waived and no 367 | failure to comply consented to unless expressly agreed to by the 368 | Licensor. 369 | 370 | d. Nothing in this Public License constitutes or may be interpreted 371 | as a limitation upon, or waiver of, any privileges and immunities 372 | that apply to the Licensor or You, including from the legal 373 | processes of any jurisdiction or authority. 374 | 375 | 376 | ======================================================================= 377 | 378 | Creative Commons is not a party to its public 379 | licenses. Notwithstanding, Creative Commons may elect to apply one of 380 | its public licenses to material it publishes and in those instances 381 | will be considered the “Licensor.” The text of the Creative Commons 382 | public licenses is dedicated to the public domain under the CC0 Public 383 | Domain Dedication. Except for the limited purpose of indicating that 384 | material is shared under a Creative Commons public license or as 385 | otherwise permitted by the Creative Commons policies published at 386 | creativecommons.org/policies, Creative Commons does not authorize the 387 | use of the trademark "Creative Commons" or any other trademark or logo 388 | of Creative Commons without its prior written consent including, 389 | without limitation, in connection with any unauthorized modifications 390 | to any of its public licenses or any other arrangements, 391 | understandings, or agreements concerning use of licensed material. For 392 | the avoidance of doubt, this paragraph does not form part of the 393 | public licenses. 394 | 395 | Creative Commons may be contacted at creativecommons.org. 396 | 397 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Maintainers 2 | 3 | ## Active Maintainers 4 | 5 | 6 | 7 | | Name | Github | LFID | 8 | | ---------------- | ---------------- | ---------------- | 9 | | Daniel Bluhm | dbluhm | | 10 | | Dominic Wörner | domwoe | | 11 | | Paul Bastian | paulbastian | | 12 | | Stephen Curran | swcurran | | 13 | 14 | ## Emeritus Maintainers 15 | 16 | | Name | Github | LFID | 17 | |--------------|---------|---------| 18 | | | | | 19 | 20 | ## Becoming a Maintainer 21 | 22 | The Indy community welcomes contributions. Contributors may progress to become a 23 | maintainer. To become a maintainer the following steps occur, roughly in order. 24 | 25 | - 5 significant changes have been authored by the proposed maintainer and 26 | accepted. 27 | - The proposed maintainer has the sponsorship of at least one other maintainer. 28 | - This sponsoring maintainer will create a PR modifying the list of 29 | maintainers. 30 | - The proposed maintainer accepts the nomination and expresses a willingness 31 | to be a long-term (more than 6 month) committer. 32 | - This would be a comment in the above PR. 33 | - This PR will be communicated in all appropriate communication channels. It 34 | should be mentioned in any maintainer/community call. It should also be 35 | posted to the appropriate mailing list or chat channels if they exist. 36 | - Approval by at least 3 current maintainers within two weeks of the proposal or 37 | an absolute majority of current maintainers. 38 | - These votes will be recorded in the PR modifying the list of maintainers. 39 | - No veto by another maintainer within two weeks of proposal are recorded. 40 | - All vetoes must be accompanied by a public explanation as a comment in the 41 | PR for adding this maintainer 42 | - The explanation of the veto must be reasonable. 43 | - A veto can be retracted, in that case the approval/veto timeframe is reset. 44 | - It is bad form to veto, retract, and veto again. 45 | - The proposed maintainer becomes a maintainer 46 | - Either two weeks have passed since the third approval, 47 | - Or an absolute majority of maintainers approve. 48 | - In either case, no maintainer presents a veto. 49 | 50 | ## Removing Maintainers 51 | 52 | Being a maintainer is not a status symbol or a title to be maintained 53 | indefinitely. It will occasionally be necessary and appropriate to move a 54 | maintainer to emeritus status. This can occur in the following situations: 55 | 56 | - Resignation of a maintainer. 57 | - Violation of the Code of Conduct warranting removal. 58 | - Inactivity. 59 | - A general measure of inactivity will be no commits or code review comments 60 | for one reporting quarter, although this will not be strictly enforced if 61 | the maintainer expresses a reasonable intent to continue contributing. 62 | - Reasonable exceptions to inactivity will be granted for known long term 63 | leave such as parental leave and medical leave. 64 | - Other unspecified circumstances. 65 | 66 | Like adding a maintainer the record and governance process for moving a 67 | maintainer to emeritus status is recorded in the github PR making that change. 68 | 69 | Returning to active status from emeritus status uses the same steps as adding a 70 | new maintainer. Note that the emeritus maintainer already has the 5 required 71 | significant changes as there is no contribution time horizon for those. 72 | -------------------------------------------------------------------------------- /assets/compiled/head.css: -------------------------------------------------------------------------------- 1 | slide-panels{position:fixed;top:0;left:0;height:100%;width:100%;pointer-events:none;z-index:100;contain:paint}slide-panels:before{content:" ";display:block;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.3);transition:opacity .35s ease;opacity:0;cursor:pointer;pointer-events:none}slide-panels[open]:before{opacity:1;pointer-events:all}slide-panel{display:flex;flex-direction:column;box-sizing:border-box;position:absolute;top:0;left:0;bottom:0;background:#fff;box-shadow:0 0 5px 1px rgba(0,0,0,.15);transform:translate3d(-100%,0,0);transition:transform .35s ease;z-index:1;pointer-events:all}slide-panel[options~=right]{left:auto;right:0;transform:translate3d(100%,0,0)}slide-panel[open]{transform:translate3d(0,0,0)}detail-box{display:block}detail-box>header [detail-box-toggle]{width:2em;height:2em;text-align:center;cursor:pointer}detail-box>header [detail-box-toggle]:before{content:" ";display:inline-block;width:0;height:0;border-left:.55em solid transparent;border-right:.55em solid transparent;border-top:.8em solid;vertical-align:sub;cursor:pointer}detail-box[open] header [detail-box-toggle]:before{border-top:none;border-bottom:.8em solid}detail-box>section{height:0;opacity:0;min-width:100%;transition:height .3s ease,opacity .3s;overflow:hidden}detail-box[open]>section{opacity:1}tab-panels>nav{display:flex}tab-panels>nav>*{margin-left:-2px;padding:.5em 1em;background:#e0e0e0;border:1px solid #aaa;border-radius:0;cursor:pointer}tab-panels>nav>:focus{outline:0;background:#ccc}tab-panels>nav>:first-child{border-top-left-radius:5px;border-bottom-left-radius:5px}tab-panels>nav>:last-child{border-top-right-radius:5px;border-bottom-right-radius:5px}tab-panels>nav>[selected]{color:var(--themed-element-text);background:var(--themed-element-bk);border:var(--themed-element-border);opacity:.9999}tab-panels>section{display:none}tab-panels>section[selected]{display:block} 2 | code[class*=language-],pre[class*=language-]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green} 3 | @keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0} 4 | :root{--base-theme-color:207,71%;--themed-element-bk:hsl(var(--base-theme-color), 40%);--themed-element-text:#fff;--themed-element-border:1px solid hsl(var(--base-theme-color), 26%);--themed-heading-text:hsl(var(--base-theme-color), 30%);--no-color:255,255,255;--faint-color:245,245,245;--dim-color:225,225,225;--low-color:200,200,200;--mid-color:100,100,100;--high-color:50,50,50;--full-color:0,0,0;--active-color:#3aaaff;--visited-color:rgb(188, 129, 255);--green-status:rgb(0, 123, 9);--light-green-status:rgb(0, 194, 13);--page-bk:rgb(var(--no-color));--page-text:rgb(var(--full-color));--page-text-hover:rgb(var(--full-color));--element-bk:rgb(var(--no-color));--element-bk-hover:rgba(var(--low-color), 0.5);--element-bk-transparent:rgba(var(--dim-color), 0.92);--element-border:rgba(var(--high-color), 0.4);--element-border-focus:rgb(var(--full-color), 0.75);--element-border-radius:3px;--element-shadow-low:0 1px 3px 0px rgba(0,0,0, 0.25);--element-shadow-mid:0 1px 3px 0px rgba(0,0,0, 0.35);--element-shadow-high:0 1px 5px 0px rgba(0,0,0, 0.45);--code-bk:#1a1e23;--input-bk:rgba(var(--dim-color), 0.6);--input-border:rgba(var(--high-color), 0.4);--header-height:48px;--header-bk:rgba(var(--low-color), 0.985);--header-text:rgb(var(--full-color));--header-border:rgba(var(--full-color), 0.1);--header-border-inverse:rgba(var(--no-color), 0.3);--text-shadow:0 1px 2px rgba(0,0,0,0.8);--svg-size:2vw;--font-size:14px}:target{scroll-margin:calc(var(--header-height)/ .75) 0 0}body:not([hashscroll]) :target{animation:highlight 1.5s .25s ease}body{margin:0;padding:0;font-family:Heebo,san-serif;line-height:1.5em;widows:2;orphans:2;word-wrap:break-word;overflow-wrap:break-word;color:#000;word-spacing:1px;counter-reset:h2 toc1}h1{font-size:2em;font-weight:700;line-height:1.2em}h2{margin:1.5em 0 1em}blockquote{position:relative;padding:0;margin:1.75em .75em;color:rgb(var(--mid-color));background:rgb(var(--faint-color))}blockquote:after,blockquote:before{content:"“";position:absolute;top:.065em;left:.065em;font-size:3em;height:.34em;line-height:100%;color:rgb(var(--low-color))}blockquote:after{content:"”";top:auto;left:auto;bottom:.065em;right:.065em;text-align:center}blockquote>p{padding:.6em 1.8em .5em 1.8em}strong strong{font-size:.9em;color:#b30032;font-weight:400;text-transform:uppercase}main article>ol,main article>ul{padding:0 0 0 2em}main article h1,main article h2,main article h3,main article h4,main article h5,main article h6{color:var(--themed-heading-text)}main article h2,main article h3,main article h4{display:flex;font-weight:500}main article h2{counter-reset:h3 h4}main article h3{counter-reset:h4}main article h2:after{counter-increment:h2;content:counter(h2) ".";padding:0 .4em 0 .2em;order:-1}main article h3:after{counter-increment:h3;content:counter(h2) "." counter(h3);padding:0 .45em 0 .2em;order:-1}main article h4:after{counter-increment:h4;content:counter(h2) "." counter(h3) "." counter(h4);padding:0 .5em 0 .2em;order:-1}h1 .toc-anchor{display:none}.toc-anchor{margin:-.1em 0 0;font-size:.875em;color:inherit;text-decoration:none;opacity:.35;order:-1;transition:opacity .3s ease}.toc-anchor:hover{opacity:1}pre{overflow:auto}code{padding:.085em .3em .1em;font-size:1.075em;color:#c7001c;vertical-align:middle;background:#f0f0f0;border-radius:4px}pre code{background:unset;padding:unset;border-radius:unset}h1 code,h2 code,h3 code,h4 code,h5 code,h6 code{font-size:1.25em;margin:-.11em .3em 0 0;border-radius:3px}ol,ul{margin:0;padding:0 0 0 1.2em}dt{font-weight:700;margin:1em 0 0}dd{margin-left:1.5em}main{box-sizing:border-box;float:right;width:75%;min-width:calc(100% - 325px);max-width:calc(100% - 275px);padding:.5em 2em 1.5em 2em;background:#fff;box-shadow:0 0 5px -1px rgba(0,0,0,.3)}main table{display:block;width:-webkit-fill-available;width:fit-content;max-width:100%;margin:1.5em 0 1.75em;border-spacing:0;border-collapse:collapse;overflow-x:auto;word-wrap:normal;overflow-wrap:normal;hyphens:manual}main thead tr th{color:var(--themed-element-text);background:var(--themed-element-bk);border:var(--themed-element-border);text-shadow:0 1px 1px rgba(0,0,0,.5)}main tr{border-top:1px solid #ccc;background-color:#fff;margin:0;padding:0}main tr:nth-child(2n){background-color:#f0f0f0}main tr th{font-weight:400;border:1px solid #ccc;text-align:left;margin:0;padding:6px 13px}main td,main th{padding:9px 13px;border:1px solid #d8d8d8}main tr td{border:1px solid #ccc;text-align:left;margin:0;padding:.55em .75em .55em}main tr td :first-child,main tr th :first-child{margin-top:0}main tr td :last-child,main tr th :last-child{margin-bottom:0}table pre[class*=language-]{border:none;border-radius:0}table pre[class*=language-]:before{display:none}svg[icon]{width:1.25em;height:1.25em;vertical-align:text-top;pointer-events:none}article p>img{max-width:100%;margin:0 auto}article li{margin-top:.4em}slide-panel>:not(header):not(footer){flex:1}:not(pre)>code[class*=language-],pre[class*=language-]{padding:.65em .8em .8em;background:var(--code-bk)}.tippy-box{box-shadow:var(--element-shadow-mid)}.tippy-box a{color:var(--active-color)}.tippy-box a:visited{color:var(--visited-color)}.tippy-content{padding:.55em .55em .5em}.tippy-content header{margin:0 0 .4em;padding:.15em .3em .1em;border-radius:2px;background:rgba(255,255,255,.1);text-shadow:0 1px rgba(0,0,0,.9)}.tippy-content table,.tippy-content tbody,.tippy-content td,.tippy-content tr{margin:0;padding:0;border:none;border-spacing:0;border-collapse:collapse;background:0 0!important;background-color:transparent!important}.tippy-content table{margin:0 .3em}.tippy-content td{font-size:.9em;padding:.2em 0 0}.tippy-content td:first-child{padding-right:.5em}a[path-0$="github.com"]:before{content:"\f09b";color:var(--page-text);margin:0 .25em 0 0;font-family:FontAwesome;text-decoration:none;display:inline-block;vertical-align:bottom}a[path-0$="github.com"][path-3=issues][path-4],a[path-0$="github.com"][path-3=projects],a[path-0$="github.com"][path-3=pull],a[path-0$="github.com"][path-3=releases]{text-decoration:none}a[path-0$="github.com"][path-3=issues][path-4] span,a[path-0$="github.com"][path-3=projects] span,a[path-0$="github.com"][path-3=pull] span,a[path-0$="github.com"][path-3=releases] span{display:none}a[path-0$="github.com"][path-3=issues][path-4]:after{content:"Issue #" attr(path-4)}a[path-0$="github.com"][path-3=pull]:after{content:"Pull Request #" attr(path-4)}a[path-0$="github.com"][path-3=releases][path-5]:after{content:"Release " attr(path-5)}a[path-0$="github.com"][path-3=projects]:after{content:"Project #" attr(path-4)}[issue-count]:after{content:"Issues (" attr(issue-count) ")";margin:0 0 0 .3em;padding:.1em 0 0}[issue-count=""][animate]{display:none;opacity:0}[issue-count][animate]:not([issue-count=""]){animation:display-show 1s}[panel-toggle]{cursor:pointer}.panel-header{display:flex;align-items:center;height:var(--header-height)}.panel-header>*{display:flex;height:100%;padding:.1em .8em 0;align-items:center}.slide-panel{width:calc(100% - 1em);max-width:475px;transition:transform .35s ease}.slide-panel[panel-open]{transform:translateX(0)}.notice{margin:1em 0;padding:.5em .9em .55em .65em;border-left:.5em solid}.notice p{margin:.4em 0 0}.note{background:#e9fbe9;border-color:#52e052}.note .notice-link{display:block;color:#178217}.issue{background:#e9f0fb;border-color:#527fe0}.issue .notice-link:before{display:block;color:#1e4cae}.warning{background:#fbe9e9;border-color:#e05252}.warning .notice-link{display:block;color:#ae1e1e}.example{color:#cebe00;background:#1a1e23;border-left:.5em solid}.example .notice-link{display:block;color:inherit;font-size:1.1em;font-family:Heebo,san-serif}.example pre[class*=language-]{padding:0;border-radius:0}.todo{background:#fbe4ff;border-color:#9700e2}.todo .notice-link{display:block;color:#6d00a2}.mermaid{display:flex;align-items:center;justify-content:center;margin:1.5em 0 1.75em}.reference-list{margin:0;padding:0;list-style:none}.reference-list dd a,.reference-status{font-style:italic}.reference-status{color:var(--green-status)}.tippy-box .reference-status{color:var(--light-green-status)}code[class*=language-],pre,pre[class*=language-]{font-size:.9em;margin:1em 0 1.5em;border-radius:3px}.example code[class*=language-],.example pre,.example pre[class*=language-]{margin:0}#svg{display:none}#header{position:sticky;position:-webkit-sticky;padding:0;top:0;margin:-.5em -2em 0 -2em;background:rgba(255,255,255,.9);border-bottom:1px solid rgba(0,0,0,.175);box-shadow:0 1px 3px 1px rgba(0,0,0,.1);z-index:2}#logo{box-sizing:border-box;display:flex;align-items:center;height:100%;padding:.5em}#logo img{height:100%}#logo+span{margin-left:auto}#header #toc_toggle{display:none;padding:0 1em;border-right:1px solid rgba(0,0,0,.15)}#content{max-width:800px}#content h1:first-of-type{margin:1em 0 .5em}#content h1:first-of-type .markdownIt-Anchor{display:none}#repo_issues{width:calc(100% - 1.5em);max-width:450px;border-left:1px solid rgba(0,0,0,.15)}#repo_issues>header{background:#eee;border-bottom:1px solid #ddd}#repo_issues>header span:first-of-type{font-weight:700;padding-top:.1em}#repo_issues>header .repo-issue-toggle{margin-left:auto;color:inherit;font-weight:700;text-decoration:none}#repo_issue_list{list-style:none;margin:0;padding:0 1.25em 1.25em;font-size:.85em;overflow:auto;-ms-overflow-style:none;scrollbar-width:none}#repo_issue_list::-webkit-scrollbar{display:none}#repo_issue_list:empty:before{content:"No issues found";display:block;text-align:center;font-size:1.1em;color:#aaa;margin:1em 0 0}.repo-issue detail-box{display:flex;flex-direction:column;padding:1em 0;border-bottom:1px solid #ddd}.repo-issue detail-box>section{order:1}.repo-issue detail-box>section:empty+.repo-issue-title [detail-box-toggle]{display:none}.repo-issue-title{display:flex;align-items:center}.repo-issue-link{flex:1;margin:0 0 0 .5em}.repo-issue-number{height:1em;margin:0 .4em 0 0;padding:.3em .25em 0;border-radius:3px;font-weight:700;background:#eee;border:1px solid #ddd;text-align:center;line-height:1em}.repo-issue-number:before{content:"#";font-weight:400;margin:0 .1em 0 0}.repo-issue [detail-box-toggle]{margin:0 0 0 1em;opacity:.35;transition:opacity .4s}.repo-issue [detail-box-toggle]:hover,.repo-issue detail-box[open] [detail-box-toggle]{opacity:1}#toc{display:flex;flex-direction:column;width:25%;max-width:325px;min-width:275px;background:#eceff1}#toc header{color:var(--themed-element-text);background:var(--themed-element-bk);box-shadow:0 1px 3px 0 rgba(0,0,0,.3);border:var(--themed-element-border);border-top:none;border-left:none}#toc header [panel-toggle]{display:none;height:var(--header-height);line-height:var(--header-height);margin-left:auto;padding:0 1em;color:inherit;font-weight:700;text-decoration:none}#toc_list{flex:1;padding:1em .8em;overflow:auto}.toc{padding:0 0 1.75em;font-size:.85em}.toc,.toc ul{margin:0;list-style:none}.toc ul{padding:0 0 0 1em}.toc a{display:block;padding:.4em .3em .225em;text-decoration:none;border-radius:3px;color:#333}.toc a:before{color:#000;font-weight:700}.toc a:hover{text-shadow:0 1px 1px #fff;background:rgba(0,0,0,.1)}.toc>li a:before{counter-increment:toc1;content:counter(toc1) ".";padding:0 .4em 0 .2em}.toc>li>ul{counter-reset:toc2}.toc>li>ul>li a:before{counter-increment:toc2;content:counter(toc1) "." counter(toc2);padding:0 .45em 0 .2em}.toc>li>ul ul{counter-reset:toc3}.toc>li>ul ul li a:before{counter-increment:toc3;content:counter(toc1) "." counter(toc2) "." counter(toc3);padding:0 .5em 0 .2em}@media (min-width:900px){slide-panel{z-index:2}#slidepanels[open=sidebar]:before{opacity:0;transition:none;pointer-events:none}#slidepanels:before{z-index:1}#toc{transition:none;transform:translate3d(0,0,0);box-shadow:0 0 5px 1px rgba(0,0,0,.15) inset;z-index:0}}@media (max-width:900px){main{width:100%;min-width:auto;max-width:none;padding:.5em 1.25em 1.5em 1.25em}#header{margin:-.5em -1.25em 0 -1.25em}#toc header [panel-toggle]{display:block}#header #toc_toggle{display:flex}}@media (max-width:550px){td{font-size:.8em}}@keyframes display-show{0%{display:none;opacity:0}1%{display:block}100%{opacity:1}}@keyframes highlight{50%{background-color:#ff0}} -------------------------------------------------------------------------------- /assets/compiled/head.js: -------------------------------------------------------------------------------- 1 | function delegateEvent(e,t,n,a={}){return(a.container||document).addEventListener(e,e=>{let a=e.target.closest(t);a&&n(e,a)},a)}skipAnimationFrame=e=>requestAnimationFrame(()=>requestAnimationFrame(e));var domReady=new Promise(e=>{document.addEventListener("DOMContentLoaded",t=>e())}); 2 | customElements.define("slide-panels",class extends HTMLElement{static get observedAttributes(){return["open"]}constructor(){super(),this.addEventListener("pointerup",e=>{e.target===this&&this.close()})}get active(){return this.getAttribute("open")}toggle(e){this.active===e?this.close():this.open(e)}open(e){this.setAttribute("open",e)}close(){this.removeAttribute("open")}attributeChangedCallback(e,t,s){switch(e){case"open":for(let e of this.children)e.id===s?e.setAttribute("open",""):e.removeAttribute("open","")}}}),customElements.define("detail-box",class extends HTMLElement{static get observedAttributes(){return["open"]}constructor(){super(),this.addEventListener("pointerup",e=>{e.target.hasAttribute("detail-box-toggle")&&(e.stopPropagation(),this.toggle())}),this.addEventListener("transitionend",e=>{let t=e.target;t.parentElement===this&&"SECTION"===t.tagName&&"height"===e.propertyName&&(t.style.height=this.hasAttribute("open")?"auto":null)})}toggle(){this.toggleAttribute("open")}attributeChangedCallback(e,t,s){switch(e){case"open":for(let e of this.children)if("SECTION"===e.tagName){if(null!==s)e.offsetHeight0){e.style.height=e.offsetHeight+"px";this.scrollHeight;e.style.height=0}break}}}}),customElements.define("tab-panels",class extends HTMLElement{constructor(){super(),delegateEvent("click","tab-panels > nav > *",(e,t)=>{let s=t.parentElement;s.parentElement===this&&this.setAttribute("selected-index",Array.prototype.indexOf.call(s.children,t))},{container:this,passive:!0})}static get observedAttributes(){return["selected-index"]}attributeChangedCallback(e,t,s){domReady.then(()=>{switch(e){case"selected-index":let e=s||0,t=this.querySelector("nav");if(t.parentElement===this){let s=t.children,i=s[e];for(let e of s)e.removeAttribute("selected");i&&i.setAttribute("selected","");let r=Array.prototype.filter.call(this.children,e=>{if("SECTION"===e.tagName)return e.removeAttribute("selected"),!0})[e];r&&r.setAttribute("selected","")}}})}}); -------------------------------------------------------------------------------- /assets/css/chart.css: -------------------------------------------------------------------------------- 1 | /* 2 | * DOM element rendering detection 3 | * https://davidwalsh.name/detect-node-insertion 4 | */ 5 | @keyframes chartjs-render-animation { 6 | from { opacity: 0.99; } 7 | to { opacity: 1; } 8 | } 9 | 10 | .chartjs-render-monitor { 11 | animation: chartjs-render-animation 0.001s; 12 | } 13 | 14 | /* 15 | * DOM element resizing detection 16 | * https://github.com/marcj/css-element-queries 17 | */ 18 | .chartjs-size-monitor, 19 | .chartjs-size-monitor-expand, 20 | .chartjs-size-monitor-shrink { 21 | position: absolute; 22 | direction: ltr; 23 | left: 0; 24 | top: 0; 25 | right: 0; 26 | bottom: 0; 27 | overflow: hidden; 28 | pointer-events: none; 29 | visibility: hidden; 30 | z-index: -1; 31 | } 32 | 33 | .chartjs-size-monitor-expand > div { 34 | position: absolute; 35 | width: 1000000px; 36 | height: 1000000px; 37 | left: 0; 38 | top: 0; 39 | } 40 | 41 | .chartjs-size-monitor-shrink > div { 42 | position: absolute; 43 | width: 200%; 44 | height: 200%; 45 | left: 0; 46 | top: 0; 47 | } 48 | -------------------------------------------------------------------------------- /assets/css/custom-elements.css: -------------------------------------------------------------------------------- 1 | 2 | slide-panels { 3 | position: fixed; 4 | top: 0; 5 | left: 0; 6 | height: 100%; 7 | width: 100%; 8 | pointer-events: none; 9 | z-index: 100; 10 | contain: paint; 11 | } 12 | 13 | slide-panels:before { 14 | content: " "; 15 | display: block; 16 | position: fixed; 17 | top: 0; 18 | left: 0; 19 | width: 100%; 20 | height: 100%; 21 | background: rgba(0,0,0,0.3); 22 | transition: opacity 0.35s ease; 23 | opacity: 0; 24 | cursor: pointer; 25 | pointer-events: none; 26 | } 27 | 28 | slide-panels[open]:before { 29 | opacity: 1; 30 | pointer-events: all; 31 | } 32 | 33 | slide-panel { 34 | display: flex; 35 | flex-direction: column; 36 | box-sizing: border-box; 37 | position: absolute; 38 | top: 0; 39 | left: 0; 40 | bottom: 0; 41 | background: #fff; 42 | box-shadow: 0 0 5px 1px rgba(0,0,0,0.15); 43 | transform: translate3d(-100%, 0%, 0); 44 | transition: transform 0.35s ease; 45 | z-index: 1; 46 | pointer-events: all; 47 | } 48 | 49 | slide-panel[options~="right"] { 50 | left: auto; 51 | right: 0; 52 | transform: translate3d(100%, 0%, 0); 53 | } 54 | 55 | slide-panel[open] { 56 | transform: translate3d(0%, 0%, 0); 57 | } 58 | 59 | detail-box { 60 | display: block; 61 | } 62 | 63 | detail-box > header [detail-box-toggle] { 64 | width: 2em; 65 | height: 2em; 66 | text-align: center; 67 | cursor: pointer; 68 | } 69 | 70 | detail-box > header [detail-box-toggle]:before { 71 | content: " "; 72 | display: inline-block; 73 | width: 0; 74 | height: 0; 75 | border-left: 0.55em solid transparent; 76 | border-right: 0.55em solid transparent; 77 | border-top: 0.8em solid; 78 | vertical-align: sub; 79 | cursor: pointer; 80 | } 81 | 82 | detail-box[open] header [detail-box-toggle]:before { 83 | border-top: none; 84 | border-bottom: 0.8em solid; 85 | } 86 | 87 | detail-box > section { 88 | height: 0; 89 | opacity: 0; 90 | min-width: 100%; 91 | transition: height 0.3s ease, opacity 0.3s; 92 | overflow: hidden; 93 | } 94 | 95 | detail-box[open] > section { 96 | opacity: 1; 97 | } 98 | 99 | 100 | /* TAB-PANELS */ 101 | 102 | tab-panels > nav { 103 | display: flex; 104 | } 105 | 106 | tab-panels > nav > * { 107 | margin-left: -2px; 108 | padding: 0.5em 1em; 109 | background: #e0e0e0; 110 | border: 1px solid #aaa; 111 | border-radius: 0; 112 | cursor: pointer; 113 | } 114 | 115 | tab-panels > nav > *:focus { 116 | outline: none; 117 | background: #ccc; 118 | } 119 | 120 | tab-panels > nav > *:first-child { 121 | border-top-left-radius: 5px; 122 | border-bottom-left-radius: 5px; 123 | } 124 | 125 | tab-panels > nav > *:last-child { 126 | border-top-right-radius: 5px; 127 | border-bottom-right-radius: 5px; 128 | } 129 | 130 | tab-panels > nav > *[selected] { 131 | color: var(--themed-element-text); 132 | background: var(--themed-element-bk); 133 | border: var(--themed-element-border); 134 | opacity: 0.9999; 135 | } 136 | 137 | tab-panels > section { 138 | display: none; 139 | } 140 | 141 | tab-panels > section[selected] { 142 | display: block; 143 | } -------------------------------------------------------------------------------- /assets/css/index.css: -------------------------------------------------------------------------------- 1 | 2 | :root { 3 | 4 | --base-theme-color: 207, 71%; 5 | --themed-element-bk: hsl(var(--base-theme-color), 40%); 6 | --themed-element-text: #fff; 7 | --themed-element-border: 1px solid hsl(var(--base-theme-color), 26%); 8 | --themed-heading-text: hsl(var(--base-theme-color), 30%); 9 | 10 | --no-color: 255,255,255; 11 | --faint-color: 245,245,245; 12 | --dim-color: 225,225,225; 13 | --low-color: 200,200,200; 14 | --mid-color: 100,100,100; 15 | --high-color: 50,50,50; 16 | --full-color: 0,0,0; 17 | --active-color: #3aaaff; 18 | --visited-color: rgb(188, 129, 255); 19 | --green-status: rgb(0, 123, 9); 20 | --light-green-status: rgb(0, 194, 13); 21 | 22 | 23 | --page-bk: rgb(var(--no-color)); 24 | --page-text: rgb(var(--full-color)); 25 | --page-text-hover: rgb(var(--full-color)); 26 | 27 | --element-bk: rgb(var(--no-color)); 28 | --element-bk-hover: rgba(var(--low-color), 0.5); 29 | --element-bk-transparent: rgba(var(--dim-color), 0.92); 30 | --element-border: rgba(var(--high-color), 0.4); 31 | --element-border-focus: rgb(var(--full-color), 0.75); 32 | --element-border-radius: 3px; 33 | --element-shadow-low: 0 1px 3px 0px rgba(0,0,0, 0.25); 34 | --element-shadow-mid: 0 1px 3px 0px rgba(0,0,0, 0.35); 35 | --element-shadow-high: 0 1px 5px 0px rgba(0,0,0, 0.45); 36 | 37 | --code-bk: #1a1e23; 38 | 39 | --input-bk: rgba(var(--dim-color), 0.6); 40 | --input-border: rgba(var(--high-color), 0.4); 41 | 42 | /* --header-height: 3.5em; */ 43 | --header-height: 48px; 44 | --header-bk: rgba(var(--low-color), 0.985); 45 | --header-text: rgb(var(--full-color)); 46 | --header-border: rgba(var(--full-color), 0.1); 47 | --header-border-inverse: rgba(var(--no-color), 0.3); 48 | 49 | --text-shadow: 0 1px 2px rgba(0,0,0,0.8); 50 | 51 | --svg-size: 2vw; 52 | 53 | --font-size: 14px; 54 | } 55 | 56 | *:target { 57 | scroll-margin: calc(var(--header-height) / 0.75) 0 0; 58 | } 59 | 60 | body:not([hashscroll]) *:target { 61 | animation: highlight 1.5s 0.25s ease; 62 | } 63 | 64 | body { 65 | margin: 0; 66 | padding: 0; 67 | font-family: Heebo, san-serif; 68 | line-height: 1.5em; 69 | widows: 2; 70 | orphans: 2; 71 | word-wrap: break-word; 72 | overflow-wrap: break-word; 73 | color: black; 74 | word-spacing: 1px; 75 | counter-reset: h2 toc1; 76 | } 77 | 78 | h1 { 79 | font-size: 2em; 80 | font-weight: bold; 81 | line-height: 1.2em; 82 | } 83 | 84 | h2 { 85 | margin: 1.5em 0 1em; 86 | } 87 | 88 | blockquote { 89 | position: relative; 90 | padding: 0; 91 | margin: 1.75em 0.75em; 92 | color: rgb(var(--mid-color)); 93 | background: rgb(var(--faint-color)); 94 | } 95 | 96 | blockquote:before, blockquote:after { 97 | content: "“"; 98 | position: absolute; 99 | top: 0.065em; 100 | left: 0.065em; 101 | font-size: 3em; 102 | height: 0.34em; 103 | line-height: 100%; 104 | color: rgb(var(--low-color)); 105 | } 106 | 107 | blockquote:after { 108 | content: "”"; 109 | top: auto; 110 | left: auto; 111 | bottom: 0.065em; 112 | right: 0.065em; 113 | text-align: center; 114 | } 115 | 116 | blockquote > p { 117 | padding: 0.6em 1.8em 0.5em 1.8em; 118 | } 119 | 120 | strong strong { 121 | font-size: 0.9em; 122 | color: #b30032; 123 | font-weight: normal; 124 | text-transform: uppercase; 125 | } 126 | 127 | main article > ol, 128 | main article > ul { 129 | padding: 0 0 0 2em; 130 | } 131 | 132 | main article h1, 133 | main article h2, 134 | main article h3, 135 | main article h4, 136 | main article h5, 137 | main article h6 { 138 | color: var(--themed-heading-text); 139 | } 140 | 141 | main article h2, 142 | main article h3, 143 | main article h4 { 144 | display: flex; 145 | font-weight: 500; 146 | } 147 | 148 | main article h2 { 149 | counter-reset: h3 h4; 150 | } 151 | 152 | main article h3 { 153 | counter-reset: h4; 154 | } 155 | 156 | main article h2:after { 157 | counter-increment: h2; 158 | content: counter(h2) "."; 159 | padding: 0 0.4em 0 0.2em; 160 | order: -1; 161 | } 162 | 163 | main article h3:after { 164 | counter-increment: h3; 165 | content: counter(h2) "." counter(h3); 166 | padding: 0 0.45em 0 0.2em; 167 | order: -1; 168 | } 169 | 170 | main article h4:after { 171 | counter-increment: h4; 172 | content: counter(h2) "." counter(h3) "." counter(h4); 173 | padding: 0 0.5em 0 0.2em; 174 | order: -1; 175 | } 176 | 177 | h1 .toc-anchor { 178 | display: none; 179 | } 180 | 181 | .toc-anchor { 182 | margin: -0.1em 0 0; 183 | font-size: 0.875em; 184 | color: inherit; 185 | text-decoration: none; 186 | opacity: 0.35; 187 | order: -1; 188 | transition: opacity 0.3s ease; 189 | } 190 | 191 | .toc-anchor:hover { 192 | opacity: 1; 193 | } 194 | 195 | pre { 196 | overflow: auto; 197 | } 198 | 199 | code { 200 | padding: 0.085em 0.3em 0.1em; 201 | font-size: 1.075em; 202 | color: #c7001c; 203 | vertical-align: middle; 204 | background: #f0f0f0; 205 | border-radius: 4px; 206 | } 207 | 208 | pre code { 209 | background: unset; 210 | padding: unset; 211 | border-radius: unset; 212 | } 213 | 214 | h1 code, 215 | h2 code, 216 | h3 code, 217 | h4 code, 218 | h5 code, 219 | h6 code { 220 | font-size: 1.25em; 221 | margin: -0.11em 0.3em 0 0; 222 | border-radius: 3px; 223 | } 224 | 225 | ol, ul { 226 | margin: 0; 227 | padding: 0 0 0 1.2em; 228 | } 229 | 230 | dt { 231 | font-weight: bold; 232 | margin: 1em 0 0; 233 | } 234 | 235 | dd { 236 | margin-left: 1.5em; 237 | } 238 | 239 | main { 240 | box-sizing: border-box; 241 | float: right; 242 | width: 75%; 243 | min-width: calc(100% - 325px); 244 | max-width: calc(100% - 275px); 245 | padding: 0.5em 2em 1.5em 2em; 246 | background: #fff; 247 | box-shadow: 0px 0px 5px -1px rgba(0,0,0,0.3); 248 | } 249 | 250 | main table { 251 | display: block; 252 | width: -webkit-fill-available; 253 | width: fit-content; 254 | max-width: 100%; 255 | margin: 1.5em 0 1.75em; 256 | border-spacing: 0; 257 | border-collapse: collapse; 258 | overflow-x: auto; 259 | word-wrap: normal; 260 | overflow-wrap: normal; 261 | hyphens: manual; 262 | } 263 | 264 | main thead tr th { 265 | color: var(--themed-element-text); 266 | background: var(--themed-element-bk); 267 | border: var(--themed-element-border); 268 | text-shadow: 0px 1px 1px rgba(0,0,0,0.5); 269 | } 270 | 271 | main tr { 272 | border-top: 1px solid #cccccc; 273 | background-color: white; 274 | margin: 0; 275 | padding: 0; 276 | } 277 | 278 | main tr:nth-child(2n) { 279 | background-color: #f0f0f0; 280 | } 281 | 282 | main tr th { 283 | font-weight: normal; 284 | border: 1px solid #cccccc; 285 | text-align: left; 286 | margin: 0; 287 | padding: 6px 13px; 288 | } 289 | 290 | main td, 291 | main th { 292 | padding: 9px 13px; 293 | border: 1px solid #d8d8d8; 294 | } 295 | 296 | main tr td { 297 | border: 1px solid #ccc; 298 | text-align: left; 299 | margin: 0; 300 | padding: 0.55em 0.75em 0.55em; 301 | } 302 | 303 | main tr th :first-child, 304 | main tr td :first-child { 305 | margin-top: 0; 306 | } 307 | 308 | main tr th :last-child, 309 | main tr td :last-child { 310 | margin-bottom: 0; 311 | } 312 | 313 | table pre[class*="language-"] { 314 | border: none; 315 | border-radius: 0; 316 | } 317 | 318 | table pre[class*="language-"]:before { 319 | display: none; 320 | } 321 | 322 | svg[icon] { 323 | width: 1.25em; 324 | height: 1.25em; 325 | vertical-align: text-top; 326 | pointer-events: none; 327 | } 328 | 329 | article p > img { 330 | max-width: 100%; 331 | margin: 0 auto; 332 | } 333 | 334 | article li { 335 | margin-top: 0.4em; 336 | } 337 | 338 | /* Custom Elements */ 339 | 340 | slide-panel > *:not(header):not(footer) { 341 | flex: 1; 342 | } 343 | 344 | /* Code Examples */ 345 | 346 | :not(pre) > code[class*="language-"], 347 | pre[class*="language-"] { 348 | padding: 0.65em 0.8em 0.8em; 349 | background: var(--code-bk); 350 | } 351 | 352 | /* Tooltips */ 353 | 354 | .tippy-box { 355 | box-shadow: var(--element-shadow-mid); 356 | } 357 | 358 | .tippy-box a { 359 | color: var(--active-color); 360 | } 361 | 362 | .tippy-box a:visited { 363 | color: var(--visited-color); 364 | } 365 | 366 | .tippy-content { 367 | padding: 0.55em 0.55em 0.5em; 368 | } 369 | 370 | .tippy-content header { 371 | margin: 0 0 0.4em; 372 | padding: 0.15em 0.3em 0.1em; 373 | border-radius: 2px; 374 | background: rgba(255,255,255,0.1); 375 | text-shadow: 0 1px rgba(0,0,0, 0.9); 376 | } 377 | 378 | .tippy-content table, 379 | .tippy-content tbody, 380 | .tippy-content tr, 381 | .tippy-content td { 382 | margin: 0; 383 | padding: 0; 384 | border: none; 385 | border-spacing: 0; 386 | border-collapse: collapse; 387 | background: none !important; 388 | background-color: transparent !important; 389 | } 390 | 391 | .tippy-content table { 392 | margin: 0 0.3em; 393 | } 394 | 395 | .tippy-content td { 396 | font-size: 0.9em; 397 | padding: 0.2em 0 0; 398 | } 399 | 400 | .tippy-content td:first-child { 401 | padding-right: 0.5em; 402 | } 403 | 404 | 405 | /*******************/ 406 | 407 | a[path-0$="github.com"]:before { 408 | content: "\f09b"; 409 | color: var(--page-text); 410 | margin: 0 0.25em 0 0; 411 | font-family: 'FontAwesome'; 412 | text-decoration: none; 413 | display: inline-block; 414 | vertical-align: bottom; 415 | } 416 | 417 | a[path-0$="github.com"][path-3="issues"][path-4], 418 | a[path-0$="github.com"][path-3="projects"], 419 | a[path-0$="github.com"][path-3="releases"], 420 | a[path-0$="github.com"][path-3="pull"] { 421 | text-decoration: none; 422 | } 423 | 424 | a[path-0$="github.com"][path-3="issues"][path-4] span, 425 | a[path-0$="github.com"][path-3="projects"] span, 426 | a[path-0$="github.com"][path-3="releases"] span, 427 | a[path-0$="github.com"][path-3="pull"] span { 428 | display: none; 429 | } 430 | 431 | a[path-0$="github.com"][path-3="issues"][path-4]:after { 432 | content: "Issue #" attr(path-4); 433 | } 434 | 435 | a[path-0$="github.com"][path-3="pull"]:after { 436 | content: "Pull Request #" attr(path-4); 437 | } 438 | 439 | a[path-0$="github.com"][path-3="releases"][path-5]:after { 440 | content: "Release " attr(path-5); 441 | } 442 | 443 | a[path-0$="github.com"][path-3="projects"]:after { 444 | content: "Project #" attr(path-4); 445 | } 446 | 447 | [issue-count]:after { 448 | content: "Issues (" attr(issue-count) ")"; 449 | margin: 0 0 0 0.3em; 450 | padding: 0.1em 0 0; 451 | } 452 | 453 | [issue-count=""][animate] { 454 | display: none; 455 | opacity: 0; 456 | } 457 | 458 | [issue-count][animate]:not([issue-count=""]) { 459 | animation: display-show 1s; 460 | } 461 | 462 | [panel-toggle] { 463 | cursor: pointer; 464 | } 465 | 466 | .panel-header { 467 | display: flex; 468 | align-items: center; 469 | height: var(--header-height); 470 | } 471 | 472 | .panel-header > * { 473 | display: flex; 474 | height: 100%; 475 | padding: 0.1em 0.8em 0; 476 | align-items: center; 477 | } 478 | 479 | 480 | .slide-panel { 481 | width: calc(100% - 1em); 482 | max-width: 475px; 483 | transition: transform 0.35s ease; 484 | } 485 | 486 | .slide-panel[panel-open] { 487 | transform: translateX(0%); 488 | } 489 | 490 | .notice { 491 | margin: 1em 0; 492 | padding: 0.5em 0.9em 0.55em 0.65em; 493 | border-left: .5em solid; 494 | } 495 | 496 | .notice p { 497 | margin: 0.4em 0 0; 498 | } 499 | 500 | .note { 501 | background: #E9FBE9; 502 | border-color: #52E052; 503 | } 504 | .note .notice-link { 505 | display: block; 506 | color: #178217; 507 | } 508 | 509 | .issue { 510 | background: rgb(233, 240, 251); 511 | border-color: rgb(82, 127, 224); 512 | } 513 | .issue .notice-link:before { 514 | display: block; 515 | color: rgb(30, 76, 174); 516 | } 517 | 518 | .warning { 519 | background: #FBE9E9; 520 | border-color: #E05252; 521 | } 522 | .warning .notice-link { 523 | display: block; 524 | color: #AE1E1E; 525 | } 526 | 527 | .example { 528 | color: #cebe00; 529 | background: #1a1e23; 530 | border-left: 0.5em solid; 531 | } 532 | 533 | .example .notice-link { 534 | display: block; 535 | color: inherit; 536 | font-size: 1.1em; 537 | font-family: Heebo, san-serif; 538 | } 539 | 540 | .example pre[class*="language-"] { 541 | padding: 0; 542 | border-radius: 0; 543 | } 544 | 545 | .todo { 546 | background: #fbe4ff; 547 | border-color: #9700e2; 548 | } 549 | .todo .notice-link { 550 | display: block; 551 | color: #6d00a2; 552 | } 553 | 554 | .mermaid { 555 | display: flex; 556 | align-items: center; 557 | justify-content: center; 558 | margin: 1.5em 0 1.75em; 559 | } 560 | 561 | /* Spec References */ 562 | 563 | .reference-list { 564 | margin: 0; 565 | padding: 0; 566 | list-style: none; 567 | } 568 | 569 | .reference-list dd a, 570 | .reference-status { 571 | font-style: italic; 572 | } 573 | 574 | .reference-status { 575 | color: var(--green-status); 576 | } 577 | 578 | .tippy-box .reference-status { 579 | color: var(--light-green-status); 580 | } 581 | 582 | /* Terminology References */ 583 | 584 | pre, 585 | code[class*="language-"], 586 | pre[class*="language-"] { 587 | font-size: 0.9em; 588 | margin: 1em 0 1.5em; 589 | border-radius: 3px; 590 | } 591 | 592 | .example pre, 593 | .example code[class*="language-"], 594 | .example pre[class*="language-"] { 595 | margin: 0; 596 | } 597 | 598 | #svg { 599 | display: none; 600 | } 601 | 602 | #header { 603 | position: sticky; 604 | position: -webkit-sticky; 605 | padding: 0; 606 | top: 0; 607 | margin: -0.5em -2em 0em -2em; 608 | background: rgba(255,255,255,0.9); 609 | border-bottom: 1px solid rgba(0,0,0,0.175); 610 | box-shadow: 0px 1px 3px 1px rgba(0,0,0,0.1); 611 | z-index: 2; 612 | } 613 | 614 | #logo { 615 | /* box-sizing: border-box; */ 616 | display: flex; 617 | align-items: center; 618 | height: 100%; 619 | padding: 0.5em; 620 | } 621 | 622 | #logo img { 623 | height: 150%; 624 | } 625 | 626 | 627 | #logo + span { 628 | margin-left: auto; 629 | } 630 | 631 | #header #toc_toggle { 632 | display: none; 633 | padding: 0 1em; 634 | border-right: 1px solid rgba(0,0,0,0.15); 635 | } 636 | 637 | #content { 638 | max-width: 800px; 639 | } 640 | 641 | #content h1:first-of-type { 642 | margin: 1em 0 0.5em; 643 | } 644 | 645 | #content h1:first-of-type .markdownIt-Anchor { 646 | display: none; 647 | } 648 | 649 | #repo_issues { 650 | width: calc(100% - 1.5em); 651 | max-width: 450px; 652 | border-left: 1px solid rgba(0,0,0,0.15); 653 | } 654 | 655 | #repo_issues > header { 656 | background: #eee; 657 | border-bottom: 1px solid #ddd; 658 | } 659 | 660 | #repo_issues > header span:first-of-type { 661 | font-weight: bold; 662 | padding-top: 0.1em; 663 | } 664 | 665 | #repo_issues > header .repo-issue-toggle { 666 | margin-left: auto; 667 | color: inherit; 668 | font-weight: bold; 669 | text-decoration: none; 670 | } 671 | 672 | #repo_issue_list { 673 | list-style: none; 674 | margin: 0; 675 | padding: 0 1.25em 1.25em; 676 | font-size: 0.85em; 677 | overflow: auto; 678 | -ms-overflow-style: none; /* Internet Explorer 10+ */ 679 | scrollbar-width: none; /* Firefox */ 680 | } 681 | #repo_issue_list::-webkit-scrollbar { 682 | display: none; /* Safari and Chrome */ 683 | } 684 | 685 | #repo_issue_list:empty:before { 686 | content: "No issues found"; 687 | display: block; 688 | text-align: center; 689 | font-size: 1.1em; 690 | color: #aaa; 691 | margin: 1em 0 0; 692 | } 693 | 694 | .repo-issue detail-box { 695 | display: flex; 696 | flex-direction: column; 697 | padding: 1em 0; 698 | border-bottom: 1px solid #ddd; 699 | } 700 | 701 | .repo-issue detail-box > section { 702 | order: 1; 703 | } 704 | 705 | .repo-issue detail-box > section:empty + .repo-issue-title [detail-box-toggle] { 706 | display: none; 707 | } 708 | 709 | .repo-issue-title { 710 | display: flex; 711 | align-items: center; 712 | } 713 | 714 | .repo-issue-link { 715 | flex: 1; 716 | margin: 0 0 0 0.5em; 717 | } 718 | 719 | .repo-issue-number { 720 | height: 1em; 721 | margin: 0 0.4em 0 0; 722 | padding: 0.3em 0.25em 0; 723 | border-radius: 3px; 724 | font-weight: bold; 725 | background: #eee; 726 | border: 1px solid #ddd; 727 | text-align: center; 728 | line-height: 1em; 729 | } 730 | 731 | .repo-issue-number:before { 732 | content: "#"; 733 | font-weight: normal; 734 | margin: 0 0.1em 0 0; 735 | } 736 | 737 | .repo-issue [detail-box-toggle] { 738 | margin: 0 0 0 1em; 739 | opacity: 0.35; 740 | transition: opacity 0.4s; 741 | } 742 | 743 | .repo-issue [detail-box-toggle]:hover, 744 | .repo-issue detail-box[open] [detail-box-toggle] { 745 | opacity: 1; 746 | } 747 | 748 | #toc { 749 | display: flex; 750 | flex-direction: column; 751 | width: 25%; 752 | max-width: 325px; 753 | min-width: 275px; 754 | background: #eceff1; 755 | } 756 | 757 | #toc header { 758 | color: var(--themed-element-text); 759 | background: var(--themed-element-bk); 760 | box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.3); 761 | border: var(--themed-element-border); 762 | border-top: none; 763 | border-left: none; 764 | } 765 | 766 | #toc header [panel-toggle] { 767 | display: none; 768 | height: var(--header-height); 769 | line-height: var(--header-height); 770 | margin-left: auto; 771 | padding: 0 1em; 772 | color: inherit; 773 | font-weight: bold; 774 | text-decoration: none; 775 | } 776 | 777 | #toc_list { 778 | flex: 1; 779 | padding: 1em 0.8em; 780 | overflow: auto; 781 | } 782 | 783 | .toc { 784 | padding: 0 0 1.75em; 785 | font-size: 0.85em; 786 | } 787 | 788 | .toc, .toc ul { 789 | margin: 0; 790 | list-style: none; 791 | } 792 | 793 | .toc ul { 794 | padding: 0 0 0 1em; 795 | } 796 | 797 | .toc a { 798 | display: block; 799 | padding: 0.4em 0.3em 0.225em; 800 | text-decoration: none; 801 | border-radius: 3px; 802 | color: #333; 803 | } 804 | 805 | .toc a:before { 806 | color: #000; 807 | font-weight: bold; 808 | } 809 | 810 | .toc a:hover { 811 | text-shadow: 0px 1px 1px #fff; 812 | background: rgba(0,0,0,0.1); 813 | } 814 | 815 | .toc > li a:before { 816 | counter-increment: toc1; 817 | content: counter(toc1) "."; 818 | padding: 0 0.4em 0 0.2em; 819 | } 820 | 821 | .toc > li > ul { 822 | counter-reset: toc2; 823 | } 824 | 825 | .toc > li > ul > li a:before { 826 | counter-increment: toc2; 827 | content: counter(toc1) "." counter(toc2); 828 | padding: 0 0.45em 0 0.2em; 829 | } 830 | 831 | .toc > li > ul ul { 832 | counter-reset: toc3; 833 | } 834 | 835 | .toc > li > ul ul li a:before { 836 | counter-increment: toc3; 837 | content: counter(toc1) "." counter(toc2) "." counter(toc3); 838 | padding: 0 0.5em 0 0.2em; 839 | } 840 | 841 | @media (min-width: 900px) { 842 | 843 | slide-panel { 844 | z-index: 2; 845 | } 846 | 847 | #slidepanels[open="sidebar"]:before { 848 | opacity: 0; 849 | transition: none; 850 | pointer-events: none; 851 | } 852 | 853 | #slidepanels:before { 854 | z-index: 1; 855 | } 856 | 857 | #toc { 858 | transition: none; 859 | transform: translate3d(0%, 0%, 0); 860 | box-shadow: 0 0 5px 1px rgba(0,0,0,0.15) inset; 861 | z-index: 0; 862 | } 863 | } 864 | 865 | @media (max-width: 900px) { 866 | 867 | main { 868 | width: 100%; 869 | min-width: auto; 870 | max-width: none; 871 | padding: 0.5em 1.25em 1.5em 1.25em; 872 | } 873 | 874 | #header { 875 | margin: -0.5em -1.25em 0em -1.25em; 876 | } 877 | 878 | #toc header [panel-toggle] { 879 | display: block; 880 | } 881 | 882 | #header #toc_toggle { 883 | display: flex; 884 | } 885 | } 886 | 887 | @media (max-width: 550px) { 888 | 889 | td { 890 | font-size: 0.8em; 891 | } 892 | 893 | } 894 | 895 | @keyframes display-show { 896 | 0% { 897 | display: none; 898 | opacity: 0; 899 | } 900 | 1% { 901 | display: block; 902 | } 903 | 100% { 904 | opacity: 1; 905 | } 906 | } 907 | 908 | @keyframes highlight { 909 | 50% { 910 | background-color: yellow; 911 | } 912 | } -------------------------------------------------------------------------------- /assets/css/prism.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.21.0 2 | https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript+abnf+diff+git+http+js-extras+json+json5+js-templates+regex&plugins=highlight-keywords */ 3 | /** 4 | * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML 5 | * Based on https://github.com/chriskempson/tomorrow-theme 6 | * @author Rose Pritchard 7 | */ 8 | 9 | code[class*="language-"], 10 | pre[class*="language-"] { 11 | color: #ccc; 12 | background: none; 13 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 14 | font-size: 1em; 15 | text-align: left; 16 | white-space: pre; 17 | word-spacing: normal; 18 | word-break: normal; 19 | word-wrap: normal; 20 | line-height: 1.5; 21 | 22 | -moz-tab-size: 4; 23 | -o-tab-size: 4; 24 | tab-size: 4; 25 | 26 | -webkit-hyphens: none; 27 | -moz-hyphens: none; 28 | -ms-hyphens: none; 29 | hyphens: none; 30 | 31 | } 32 | 33 | /* Code blocks */ 34 | pre[class*="language-"] { 35 | padding: 1em; 36 | margin: .5em 0; 37 | overflow: auto; 38 | } 39 | 40 | :not(pre) > code[class*="language-"], 41 | pre[class*="language-"] { 42 | background: #2d2d2d; 43 | } 44 | 45 | /* Inline code */ 46 | :not(pre) > code[class*="language-"] { 47 | padding: .1em; 48 | border-radius: .3em; 49 | white-space: normal; 50 | } 51 | 52 | .token.comment, 53 | .token.block-comment, 54 | .token.prolog, 55 | .token.doctype, 56 | .token.cdata { 57 | color: #999; 58 | } 59 | 60 | .token.punctuation { 61 | color: #ccc; 62 | } 63 | 64 | .token.tag, 65 | .token.attr-name, 66 | .token.namespace, 67 | .token.deleted { 68 | color: #e2777a; 69 | } 70 | 71 | .token.function-name { 72 | color: #6196cc; 73 | } 74 | 75 | .token.boolean, 76 | .token.number, 77 | .token.function { 78 | color: #f08d49; 79 | } 80 | 81 | .token.property, 82 | .token.class-name, 83 | .token.constant, 84 | .token.symbol { 85 | color: #f8c555; 86 | } 87 | 88 | .token.selector, 89 | .token.important, 90 | .token.atrule, 91 | .token.keyword, 92 | .token.builtin { 93 | color: #cc99cd; 94 | } 95 | 96 | .token.string, 97 | .token.char, 98 | .token.attr-value, 99 | .token.regex, 100 | .token.variable { 101 | color: #7ec699; 102 | } 103 | 104 | .token.operator, 105 | .token.entity, 106 | .token.url { 107 | color: #67cdcc; 108 | } 109 | 110 | .token.important, 111 | .token.bold { 112 | font-weight: bold; 113 | } 114 | .token.italic { 115 | font-style: italic; 116 | } 117 | 118 | .token.entity { 119 | cursor: help; 120 | } 121 | 122 | .token.inserted { 123 | color: green; 124 | } 125 | 126 | -------------------------------------------------------------------------------- /assets/icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/js/custom-elements.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | customElements.define('slide-panels', class SidePanels extends HTMLElement { 5 | static get observedAttributes() { 6 | return ['open']; 7 | } 8 | constructor() { 9 | super(); 10 | 11 | this.addEventListener('pointerup', e => { 12 | if (e.target === this) this.close(); 13 | }) 14 | } 15 | get active (){ 16 | return this.getAttribute('open'); 17 | } 18 | toggle(panel){ 19 | this.active === panel ? this.close() : this.open(panel) 20 | } 21 | open (panel){ 22 | this.setAttribute('open', panel); 23 | } 24 | close (){ 25 | this.removeAttribute('open'); 26 | } 27 | attributeChangedCallback(attr, last, current) { 28 | switch(attr) { 29 | case 'open': for (let child of this.children) { 30 | if (child.id === current) child.setAttribute('open', ''); 31 | else child.removeAttribute('open', ''); 32 | } 33 | break; 34 | } 35 | } 36 | }); 37 | 38 | customElements.define('detail-box', class DetailBox extends HTMLElement { 39 | static get observedAttributes() { 40 | return ['open']; 41 | } 42 | constructor() { 43 | super(); 44 | 45 | this.addEventListener('pointerup', e => { 46 | if (e.target.hasAttribute('detail-box-toggle')) { 47 | e.stopPropagation(); 48 | this.toggle(); 49 | } 50 | }); 51 | 52 | this.addEventListener('transitionend', e => { 53 | let node = e.target; 54 | if (node.parentElement === this && node.tagName === 'SECTION' && e.propertyName === 'height') { 55 | node.style.height = this.hasAttribute('open') ? 'auto' : null; 56 | } 57 | }); 58 | } 59 | toggle(){ 60 | this.toggleAttribute('open'); 61 | } 62 | attributeChangedCallback(attr, last, current) { 63 | switch(attr) { 64 | case 'open': 65 | for (let node of this.children) { 66 | if (node.tagName === 'SECTION') { 67 | if (current !== null) { 68 | if (node.offsetHeight < node.scrollHeight) { 69 | node.style.height = node.scrollHeight + 'px'; 70 | } 71 | } 72 | else if (node.offsetHeight > 0) { 73 | node.style.height = node.offsetHeight + 'px'; 74 | let scroll = this.scrollHeight; 75 | node.style.height = 0; 76 | } 77 | break; 78 | } 79 | } 80 | } 81 | } 82 | }); 83 | 84 | customElements.define('tab-panels', class TabPanels extends HTMLElement { 85 | constructor() { 86 | super(); 87 | delegateEvent('click', 'tab-panels > nav > *', (e, delegate) => { 88 | let nav = delegate.parentElement; 89 | if (nav.parentElement === this) { 90 | this.setAttribute('selected-index', Array.prototype.indexOf.call(nav.children, delegate)) 91 | } 92 | }, { container: this, passive: true }); 93 | } 94 | static get observedAttributes() { 95 | return ['selected-index']; 96 | } 97 | attributeChangedCallback(attr, last, current) { 98 | domReady.then(() => { 99 | switch(attr) { 100 | case 'selected-index': 101 | let index = current || 0; 102 | let nav = this.querySelector('nav'); 103 | if (nav.parentElement === this) { 104 | let tabs = nav.children; 105 | let selected = tabs[index]; 106 | for (let tab of tabs) tab.removeAttribute('selected'); 107 | if (selected) selected.setAttribute('selected', ''); 108 | let panel = Array.prototype.filter.call(this.children, node => { 109 | if (node.tagName === 'SECTION') { 110 | node.removeAttribute('selected'); 111 | return true; 112 | } 113 | })[index]; 114 | if (panel) panel.setAttribute('selected', ''); 115 | } 116 | break; 117 | } 118 | }); 119 | } 120 | }); -------------------------------------------------------------------------------- /assets/js/font-awesome.js: -------------------------------------------------------------------------------- 1 | window.FontAwesomeKitConfig = {"asyncLoading":{"enabled":true},"autoA11y":{"enabled":true},"baseUrl":"https://kit-free.fontawesome.com","detectConflictsUntil":null,"license":"free","method":"css","minify":{"enabled":true},"v4FontFaceShim":{"enabled":true},"v4shim":{"enabled":false},"version":"latest"}; 2 | !function(){function r(e){var t,n=[],i=document,o=i.documentElement.doScroll,r="DOMContentLoaded",a=(o?/^loaded|^c/:/^loaded|^i|^c/).test(i.readyState);a||i.addEventListener(r,t=function(){for(i.removeEventListener(r,t),a=1;t=n.shift();)t()}),a?setTimeout(e,0):n.push(e)}!function(){if(!(void 0===window.Element||"classList"in document.documentElement)){var e,t,n,i=Array.prototype,o=i.push,r=i.splice,a=i.join;d.prototype={add:function(e){this.contains(e)||(o.call(this,e),this.el.className=this.toString())},contains:function(e){return-1!=this.el.className.indexOf(e)},item:function(e){return this[e]||null},remove:function(e){if(this.contains(e)){for(var t=0;t { 9 | slidepanels.toggle(delegate.getAttribute('panel-toggle')); 10 | }, { passive: true }); 11 | 12 | window.addEventListener('hashchange', (e) => slidepanels.close()); 13 | 14 | /* GitHub Issues */ 15 | 16 | let source = specConfig.source; 17 | if (source) { 18 | if (source.host === 'github') { 19 | fetch(`https://api.github.com/repos/${ source.account + '/' + source.repo }/issues`) 20 | .then(response => response.json()) 21 | .then(issues => { 22 | let count = issues.length; 23 | document.querySelectorAll('[issue-count]').forEach(node => { 24 | node.setAttribute('issue-count', count) 25 | }); 26 | repo_issue_list.innerHTML = issues.map(issue => { 27 | return `
  • 28 | 29 |
    ${markdown.render(issue.body)}
    30 |
    31 | ${issue.number} 32 | 33 | ${issue.title} 34 | 35 | 36 |
    37 |
    38 |
  • ` 39 | }).join(''); 40 | Prism.highlightAllUnder(repo_issue_list); 41 | }) 42 | } 43 | } 44 | //${markdown.render(issue.body)} 45 | 46 | /* Mermaid Diagrams */ 47 | 48 | mermaid.initialize({ 49 | startOnLoad: true, 50 | theme: 'neutral' 51 | }); 52 | 53 | /* Charts */ 54 | 55 | document.querySelectorAll('.chartjs').forEach(chart => { 56 | new Chart(chart, JSON.parse(chart.textContent)); 57 | }); 58 | 59 | /* Tooltips */ 60 | let tipMap = new WeakMap(); 61 | delegateEvent('pointerover', '.term-reference, .spec-reference', (e, anchor) => { 62 | let term = document.getElementById((anchor.getAttribute('href') || '').replace('#', '')); 63 | if (!term || tipMap.has(anchor)) return; 64 | let container = term.closest('dt, td:first-child'); 65 | if (!container) return; 66 | let tip = { 67 | allowHTML: true, 68 | inlinePositioning: true 69 | } 70 | switch (container.tagName) { 71 | case 'DT': 72 | tip.content = container.nextElementSibling.textContent; 73 | break; 74 | case 'TD': 75 | let table = container.closest('table'); 76 | let tds = Array.from(container.closest('tr').children); 77 | tds.shift(); 78 | if (table) { 79 | let headings = Array.from(table.querySelectorAll('thead th')); 80 | headings.shift(); 81 | if (headings.length) { 82 | tip.content = ` 83 |
    ${container.textContent}
    84 | 85 | ${headings.map((th, i) => { 86 | return `` 87 | }).join('')} 88 |
    ${th.textContent}:${tds[i] ? tds[i].textContent : ''}
    `; 89 | } 90 | } 91 | break; 92 | } 93 | if (tip.content) tipMap.set(anchor, tippy(anchor, tip)); 94 | }, { passive: true }); 95 | 96 | 97 | })(); 98 | -------------------------------------------------------------------------------- /assets/js/popper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @popperjs/core v2.5.3 - MIT License 3 | */ 4 | 5 | "use strict";!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).Popper={})}(this,(function(e){function t(e){return{width:(e=e.getBoundingClientRect()).width,height:e.height,top:e.top,right:e.right,bottom:e.bottom,left:e.left,x:e.left,y:e.top}}function n(e){return"[object Window]"!==e.toString()?(e=e.ownerDocument)&&e.defaultView||window:e}function r(e){return{scrollLeft:(e=n(e)).pageXOffset,scrollTop:e.pageYOffset}}function o(e){return e instanceof n(e).Element||e instanceof Element}function i(e){return e instanceof n(e).HTMLElement||e instanceof HTMLElement}function a(e){return e?(e.nodeName||"").toLowerCase():null}function s(e){return((o(e)?e.ownerDocument:e.document)||window.document).documentElement}function f(e){return t(s(e)).left+r(e).scrollLeft}function c(e){return n(e).getComputedStyle(e)}function p(e){return e=c(e),/auto|scroll|overlay|hidden/.test(e.overflow+e.overflowY+e.overflowX)}function l(e,o,c){void 0===c&&(c=!1);var l=s(o);e=t(e);var u=i(o),d={scrollLeft:0,scrollTop:0},m={x:0,y:0};return(u||!u&&!c)&&(("body"!==a(o)||p(l))&&(d=o!==n(o)&&i(o)?{scrollLeft:o.scrollLeft,scrollTop:o.scrollTop}:r(o)),i(o)?((m=t(o)).x+=o.clientLeft,m.y+=o.clientTop):l&&(m.x=f(l))),{x:e.left+d.scrollLeft-m.x,y:e.top+d.scrollTop-m.y,width:e.width,height:e.height}}function u(e){return{x:e.offsetLeft,y:e.offsetTop,width:e.offsetWidth,height:e.offsetHeight}}function d(e){return"html"===a(e)?e:e.assignedSlot||e.parentNode||e.host||s(e)}function m(e,t){void 0===t&&(t=[]);var r=function e(t){return 0<=["html","body","#document"].indexOf(a(t))?t.ownerDocument.body:i(t)&&p(t)?t:e(d(t))}(e);e="body"===a(r);var o=n(r);return r=e?[o].concat(o.visualViewport||[],p(r)?r:[]):r,t=t.concat(r),e?t:t.concat(m(d(r)))}function h(e){if(!i(e)||"fixed"===c(e).position)return null;if(e=e.offsetParent){var t=s(e);if("body"===a(e)&&"static"===c(e).position&&"static"!==c(t).position)return t}return e}function g(e){for(var t=n(e),r=h(e);r&&0<=["table","td","th"].indexOf(a(r))&&"static"===c(r).position;)r=h(r);if(r&&"body"===a(r)&&"static"===c(r).position)return t;if(!r)e:{for(e=d(e);i(e)&&0>["html","body"].indexOf(a(e));){if("none"!==(r=c(e)).transform||"none"!==r.perspective||r.willChange&&"auto"!==r.willChange){r=e;break e}e=e.parentNode}r=null}return r||t}function v(e){var t=new Map,n=new Set,r=[];return e.forEach((function(e){t.set(e.name,e)})),e.forEach((function(e){n.has(e.name)||function e(o){n.add(o.name),[].concat(o.requires||[],o.requiresIfExists||[]).forEach((function(r){n.has(r)||(r=t.get(r))&&e(r)})),r.push(o)}(e)})),r}function b(e){var t;return function(){return t||(t=new Promise((function(n){Promise.resolve().then((function(){t=void 0,n(e())}))}))),t}}function y(e){return e.split("-")[0]}function O(e,t){var r=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if(r instanceof n(r).ShadowRoot||r instanceof ShadowRoot)do{if(t&&e.isSameNode(t))return!0;t=t.parentNode||t.host}while(t);return!1}function w(e){return Object.assign(Object.assign({},e),{},{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function x(e,o){if("viewport"===o){o=n(e);var a=s(e);o=o.visualViewport;var p=a.clientWidth;a=a.clientHeight;var l=0,u=0;o&&(p=o.width,a=o.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(l=o.offsetLeft,u=o.offsetTop)),e=w(e={width:p,height:a,x:l+f(e),y:u})}else i(o)?((e=t(o)).top+=o.clientTop,e.left+=o.clientLeft,e.bottom=e.top+o.clientHeight,e.right=e.left+o.clientWidth,e.width=o.clientWidth,e.height=o.clientHeight,e.x=e.left,e.y=e.top):(u=s(e),e=s(u),l=r(u),o=u.ownerDocument.body,p=Math.max(e.scrollWidth,e.clientWidth,o?o.scrollWidth:0,o?o.clientWidth:0),a=Math.max(e.scrollHeight,e.clientHeight,o?o.scrollHeight:0,o?o.clientHeight:0),u=-l.scrollLeft+f(u),l=-l.scrollTop,"rtl"===c(o||e).direction&&(u+=Math.max(e.clientWidth,o?o.clientWidth:0)-p),e=w({width:p,height:a,x:u,y:l}));return e}function j(e,t,n){return t="clippingParents"===t?function(e){var t=m(d(e)),n=0<=["absolute","fixed"].indexOf(c(e).position)&&i(e)?g(e):e;return o(n)?t.filter((function(e){return o(e)&&O(e,n)&&"body"!==a(e)})):[]}(e):[].concat(t),(n=(n=[].concat(t,[n])).reduce((function(t,n){return n=x(e,n),t.top=Math.max(n.top,t.top),t.right=Math.min(n.right,t.right),t.bottom=Math.min(n.bottom,t.bottom),t.left=Math.max(n.left,t.left),t}),x(e,n[0]))).width=n.right-n.left,n.height=n.bottom-n.top,n.x=n.left,n.y=n.top,n}function M(e){return 0<=["top","bottom"].indexOf(e)?"x":"y"}function E(e){var t=e.reference,n=e.element,r=(e=e.placement)?y(e):null;e=e?e.split("-")[1]:null;var o=t.x+t.width/2-n.width/2,i=t.y+t.height/2-n.height/2;switch(r){case"top":o={x:o,y:t.y-n.height};break;case"bottom":o={x:o,y:t.y+t.height};break;case"right":o={x:t.x+t.width,y:i};break;case"left":o={x:t.x-n.width,y:i};break;default:o={x:t.x,y:t.y}}if(null!=(r=r?M(r):null))switch(i="y"===r?"height":"width",e){case"start":o[r]=Math.floor(o[r])-Math.floor(t[i]/2-n[i]/2);break;case"end":o[r]=Math.floor(o[r])+Math.ceil(t[i]/2-n[i]/2)}return o}function D(e){return Object.assign(Object.assign({},{top:0,right:0,bottom:0,left:0}),e)}function P(e,t){return t.reduce((function(t,n){return t[n]=e,t}),{})}function L(e,n){void 0===n&&(n={});var r=n;n=void 0===(n=r.placement)?e.placement:n;var i=r.boundary,a=void 0===i?"clippingParents":i,f=void 0===(i=r.rootBoundary)?"viewport":i;i=void 0===(i=r.elementContext)?"popper":i;var c=r.altBoundary,p=void 0!==c&&c;r=D("number"!=typeof(r=void 0===(r=r.padding)?0:r)?r:P(r,T));var l=e.elements.reference;c=e.rects.popper,a=j(o(p=e.elements[p?"popper"===i?"reference":"popper":i])?p:p.contextElement||s(e.elements.popper),a,f),p=E({reference:f=t(l),element:c,strategy:"absolute",placement:n}),c=w(Object.assign(Object.assign({},c),p)),f="popper"===i?c:f;var u={top:a.top-f.top+r.top,bottom:f.bottom-a.bottom+r.bottom,left:a.left-f.left+r.left,right:f.right-a.right+r.right};if(e=e.modifiersData.offset,"popper"===i&&e){var d=e[n];Object.keys(u).forEach((function(e){var t=0<=["right","bottom"].indexOf(e)?1:-1,n=0<=["top","bottom"].indexOf(e)?"y":"x";u[e]+=d[n]*t}))}return u}function k(){for(var e=arguments.length,t=Array(e),n=0;n(v.devicePixelRatio||1)?"translate("+e+"px, "+l+"px)":"translate3d("+e+"px, "+l+"px, 0)",d)):Object.assign(Object.assign({},r),{},((t={})[h]=a?l+"px":"",t[m]=u?e+"px":"",t.transform="",t))}function A(e){return e.replace(/left|right|bottom|top/g,(function(e){return G[e]}))}function H(e){return e.replace(/start|end/g,(function(e){return J[e]}))}function R(e,t,n){return void 0===n&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function S(e){return["top","right","bottom","left"].some((function(t){return 0<=e[t]}))}var T=["top","bottom","right","left"],q=T.reduce((function(e,t){return e.concat([t+"-start",t+"-end"])}),[]),C=[].concat(T,["auto"]).reduce((function(e,t){return e.concat([t,t+"-start",t+"-end"])}),[]),N="beforeRead read afterRead beforeMain main afterMain beforeWrite write afterWrite".split(" "),V={placement:"bottom",modifiers:[],strategy:"absolute"},I={passive:!0},_={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(e){var t=e.state,r=e.instance,o=(e=e.options).scroll,i=void 0===o||o,a=void 0===(e=e.resize)||e,s=n(t.elements.popper),f=[].concat(t.scrollParents.reference,t.scrollParents.popper);return i&&f.forEach((function(e){e.addEventListener("scroll",r.update,I)})),a&&s.addEventListener("resize",r.update,I),function(){i&&f.forEach((function(e){e.removeEventListener("scroll",r.update,I)})),a&&s.removeEventListener("resize",r.update,I)}},data:{}},U={name:"popperOffsets",enabled:!0,phase:"read",fn:function(e){var t=e.state;t.modifiersData[e.name]=E({reference:t.rects.reference,element:t.rects.popper,strategy:"absolute",placement:t.placement})},data:{}},z={top:"auto",right:"auto",bottom:"auto",left:"auto"},F={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(e){var t=e.state,n=e.options;e=void 0===(e=n.gpuAcceleration)||e,n=void 0===(n=n.adaptive)||n,e={placement:y(t.placement),popper:t.elements.popper,popperRect:t.rects.popper,gpuAcceleration:e},null!=t.modifiersData.popperOffsets&&(t.styles.popper=Object.assign(Object.assign({},t.styles.popper),W(Object.assign(Object.assign({},e),{},{offsets:t.modifiersData.popperOffsets,position:t.options.strategy,adaptive:n})))),null!=t.modifiersData.arrow&&(t.styles.arrow=Object.assign(Object.assign({},t.styles.arrow),W(Object.assign(Object.assign({},e),{},{offsets:t.modifiersData.arrow,position:"absolute",adaptive:!1})))),t.attributes.popper=Object.assign(Object.assign({},t.attributes.popper),{},{"data-popper-placement":t.placement})},data:{}},X={name:"applyStyles",enabled:!0,phase:"write",fn:function(e){var t=e.state;Object.keys(t.elements).forEach((function(e){var n=t.styles[e]||{},r=t.attributes[e]||{},o=t.elements[e];i(o)&&a(o)&&(Object.assign(o.style,n),Object.keys(r).forEach((function(e){var t=r[e];!1===t?o.removeAttribute(e):o.setAttribute(e,!0===t?"":t)})))}))},effect:function(e){var t=e.state,n={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(t.elements.popper.style,n.popper),t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow),function(){Object.keys(t.elements).forEach((function(e){var r=t.elements[e],o=t.attributes[e]||{};e=Object.keys(t.styles.hasOwnProperty(e)?t.styles[e]:n[e]).reduce((function(e,t){return e[t]="",e}),{}),i(r)&&a(r)&&(Object.assign(r.style,e),Object.keys(o).forEach((function(e){r.removeAttribute(e)})))}))}},requires:["computeStyles"]},Y={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(e){var t=e.state,n=e.name,r=void 0===(e=e.options.offset)?[0,0]:e,o=(e=C.reduce((function(e,n){var o=t.rects,i=y(n),a=0<=["left","top"].indexOf(i)?-1:1,s="function"==typeof r?r(Object.assign(Object.assign({},o),{},{placement:n})):r;return o=(o=s[0])||0,s=((s=s[1])||0)*a,i=0<=["left","right"].indexOf(i)?{x:s,y:o}:{x:o,y:s},e[n]=i,e}),{}))[t.placement],i=o.x;o=o.y,null!=t.modifiersData.popperOffsets&&(t.modifiersData.popperOffsets.x+=i,t.modifiersData.popperOffsets.y+=o),t.modifiersData[n]=e}},G={left:"right",right:"left",bottom:"top",top:"bottom"},J={start:"end",end:"start"},K={name:"flip",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options;if(e=e.name,!t.modifiersData[e]._skip){var r=n.mainAxis;r=void 0===r||r;var o=n.altAxis;o=void 0===o||o;var i=n.fallbackPlacements,a=n.padding,s=n.boundary,f=n.rootBoundary,c=n.altBoundary,p=n.flipVariations,l=void 0===p||p,u=n.allowedAutoPlacements;p=y(n=t.options.placement),i=i||(p!==n&&l?function(e){if("auto"===y(e))return[];var t=A(e);return[H(e),t,H(t)]}(n):[A(n)]);var d=[n].concat(i).reduce((function(e,n){return e.concat("auto"===y(n)?function(e,t){void 0===t&&(t={});var n=t.boundary,r=t.rootBoundary,o=t.padding,i=t.flipVariations,a=t.allowedAutoPlacements,s=void 0===a?C:a,f=t.placement.split("-")[1];0===(i=(t=f?i?q:q.filter((function(e){return e.split("-")[1]===f})):T).filter((function(e){return 0<=s.indexOf(e)}))).length&&(i=t);var c=i.reduce((function(t,i){return t[i]=L(e,{placement:i,boundary:n,rootBoundary:r,padding:o})[y(i)],t}),{});return Object.keys(c).sort((function(e,t){return c[e]-c[t]}))}(t,{placement:n,boundary:s,rootBoundary:f,padding:a,flipVariations:l,allowedAutoPlacements:u}):n)}),[]);n=t.rects.reference,i=t.rects.popper;var m=new Map;p=!0;for(var h=d[0],g=0;gi[x]&&(O=A(O)),x=A(O),w=[],r&&w.push(0>=j[b]),o&&w.push(0>=j[O],0>=j[x]),w.every((function(e){return e}))){h=v,p=!1;break}m.set(v,w)}if(p)for(r=function(e){var t=d.find((function(t){if(t=m.get(t))return t.slice(0,e).every((function(e){return e}))}));if(t)return h=t,"break"},o=l?3:1;0 { 4 | let match = e.target.closest(selector); 5 | if (match) fn(e, match); 6 | }, options); 7 | } 8 | 9 | skipAnimationFrame = fn => requestAnimationFrame(() => requestAnimationFrame(fn)); 10 | 11 | var domReady = new Promise(resolve => { 12 | document.addEventListener('DOMContentLoaded', e => resolve()) 13 | }); -------------------------------------------------------------------------------- /custom-assets/custom-body.js: -------------------------------------------------------------------------------- 1 | console.log('Custom javascript in body') -------------------------------------------------------------------------------- /custom-assets/custom-head.js: -------------------------------------------------------------------------------- 1 | console.log('Custom javascript in head') -------------------------------------------------------------------------------- /custom-assets/custom.css: -------------------------------------------------------------------------------- 1 | 2 | body:after { 3 | content: "Custom CSS in body"; 4 | opacity: 0; 5 | pointer-events: none; 6 | } -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 2 | const yargs = require('yargs/yargs') 3 | const { hideBin } = require('yargs/helpers') 4 | const argv = yargs(hideBin(process.argv)).argv; 5 | 6 | const fs = require('fs-extra'); 7 | const gulp = require('gulp'); 8 | const run = require('gulp-run'); 9 | const bump = require('gulp-bump'); 10 | const concat = require('gulp-concat'); 11 | const terser = require('gulp-terser'); 12 | const mergeStreams = require('merge-stream'); 13 | const cleanCSS = require('gulp-clean-css'); 14 | const axios = require('axios').default; 15 | const assets = fs.readJsonSync('./src/asset-map.json'); 16 | 17 | let compileLocation = 'assets/compiled'; 18 | 19 | async function fetchSpecRefs(){ 20 | return Promise.all([ 21 | axios.get('https://ghcdn.rawgit.org/tobie/specref/master/refs/ietf.json'), 22 | axios.get('https://ghcdn.rawgit.org/tobie/specref/master/refs/w3c.json'), 23 | axios.get('https://ghcdn.rawgit.org/tobie/specref/master/refs/whatwg.json') 24 | ]).then(async results => { 25 | let json = Object.assign(results[0].data, results[1].data, results[2].data); 26 | return fs.outputFile(compileLocation + '/refs.json', JSON.stringify(json)); 27 | }).catch(e => console.log(e)); 28 | } 29 | 30 | async function compileAssets(){ 31 | await fs.ensureDir(compileLocation); 32 | return new Promise(resolve => { 33 | mergeStreams( 34 | gulp.src(assets.head.css) 35 | .pipe(cleanCSS()) 36 | .pipe(concat('head.css')) 37 | .pipe(gulp.dest(compileLocation)), 38 | gulp.src(assets.head.js) 39 | .pipe(terser()) 40 | .pipe(concat('head.js')) 41 | .pipe(gulp.dest(compileLocation)), 42 | gulp.src(assets.body.js) 43 | .pipe(terser()) 44 | .pipe(concat('body.js')) 45 | .pipe(gulp.dest(compileLocation)) 46 | ).on('finish', function() { 47 | resolve(); 48 | }) 49 | }); 50 | } 51 | 52 | async function bumpVersion(){ 53 | return gulp.src('./package.json') 54 | .pipe(bump({ type: argv.v || 'patch' })) 55 | .pipe(gulp.dest('./')); 56 | } 57 | 58 | async function renderSpecs(){ 59 | return run('npm run render').exec() 60 | } 61 | 62 | gulp.task('refs', fetchSpecRefs); 63 | 64 | gulp.task('compile', compileAssets); 65 | 66 | gulp.task('publish', gulp.series(gulp.parallel(compileAssets, bumpVersion), renderSpecs)); 67 | 68 | gulp.task('watch', () => gulp.watch([ 69 | 'assets/**/*', 70 | '!assets/css/head.css', 71 | '!assets/js/head.js', 72 | '!assets/js/body.js' 73 | ], gulp.parallel('build'))); -------------------------------------------------------------------------------- /hyperledger-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 18 | 20 | 21 | 23 | image/svg+xml 24 | 26 | hyperledger 27 | 28 | 29 | 30 | 50 | 52 | 54 | 55 | hyperledger 57 | 60 | 63 | 69 | 75 | 82 | 88 | 95 | 101 | 108 | 114 | 121 | 127 | 134 | 140 | 147 | 153 | 159 | 165 | 171 | 180 | 186 | 195 | 196 | 199 | 205 | 211 | 217 | 223 | 229 | 235 | 241 | 247 | 253 | 259 | 265 | 266 | 267 | 268 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | require('spec-up')(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "indy-did-method", 3 | "description": "DID Indy DID Method Specification", 4 | "version": "0.1.0", 5 | "homepage": "http://hyperledger.org", 6 | "license": "Apache 2", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/hyperledger/indy-did-method" 10 | }, 11 | "dependencies": { 12 | "spec-up": "0.10.6" 13 | }, 14 | "devDependencies": { 15 | "ajv": "^6.12.5", 16 | "mocha": "^8.1.3", 17 | "ace-custom-element": "1.6.5" 18 | }, 19 | "scripts": { 20 | "render": "node -e \"require('spec-up')({ nowatch: true })\"", 21 | "edit": "node -e \"require('spec-up')()\"", 22 | "test": "mocha 'test/**/*.js'" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Indy DID Method 2 | 3 | [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/hyperledger/indy-did-method) 4 | 5 | This repository contains the source for the Indy DID Method specification. The initital content was transferred from a [collaborative HackMD document](https://hackmd.io/2IKUPROnRXW57Lmal_SGaQ). 6 | 7 | The current version of the spec is rendered here: [https://hyperledger.github.io/indy-did-method/](https://hyperledger.github.io/indy-did-method/) 8 | 9 | To contribute to the specification, please submit a pull request by: 10 | 11 | - forking the repo 12 | - editing the appropriate markdown files in the `/spec` folder 13 | 14 | The specification is rendered from the markdown files to the `index.html` specification file in the `/docs` folder 15 | using [Spec-Up](https://github.com/decentralized-identity/spec-up). The full guidance for using SpecUp is in that documentation. 16 | The short version of the instructions for this repository is: 17 | 18 | - Install prerequisites: node and npm 19 | - Run `npm install` from the root of the repository 20 | - Run `npm run edit` from the root of the repository to render the document with live updates to the `docs/index.html`. You can see the updates as you make as you edit the markdown files. Open the rendered file in a browser and refresh to see the updates as you work. 21 | - You can also run `npm run render` to just generate the specification file once. 22 | 23 | When you create a PR to update the specification, please **DO NOT** include `docs/index.html`. A GitHub Action on merge of PRs automagically 24 | renders for the full specification (`docs/index.html`) to the `gh-pages` branch in the repo and the specification is 25 | published from there. If there is a way to .gitignore the `index.html` in the main branch but not in the `gh-pages` branch, please let us know. 26 | 27 | Hint: One way to revert the updated `docs/index.html` before doing a commit, is to run: `git checkout -- docs/index.html` 28 | 29 | ### Editing the spec in the cloud with Gitpod 30 | 31 | An alternative way to contribute to the specification, without any local installation, is to use [Gitpod](https://www.gitpod.io/). 32 | - Fork the repo. 33 | - Open `https://gitpod.io/#https://github.com/YOUR_FORK_USER/indy-did-method` 34 | - Register with Gitpod using your GitHub Account and provide `public_repo` permissions in order to commit to your fork from Gitpod. 35 | - You'll see a prepared VSCode workspace with two windows. To the left, you can edit the markdown files in the `/spec` folder. To the right, you'll see the rendered spec. 36 | - If the window is frozen try to reload the page. 37 | - You can create a new branch and commit and push your changes using a terminal or the source control plugin to the left. 38 | - Then, you can create a pull request from Github. 39 | 40 | ## Community Links 41 | 42 | - [Meeting Agendas and Notes](https://wiki.hyperledger.org/display/indy/Indy+DID+Method+Specification) 43 | - [Hyperledger Discord](https://discord.gg/hyperledger) 44 | 45 | -------------------------------------------------------------------------------- /spec/0_header.md: -------------------------------------------------------------------------------- 1 | Indy DID Method 2 | ================== 3 | 4 | **Specification Status:** Version 1.0 (Draft) 5 | 6 | **Latest Draft:** 7 | 8 | [https://github.com/hyperledger/indy-did-method](https://github.com/hyperledger/indy-did-method) 9 | 10 | **Editors:** 11 | 12 | - [Stephen Curran](https://github.com/swcurran) 13 | - [Paul Bastian](https://github.com/paulbastian) 14 | - [Daniel Hardman](https://github.com/dhh1128) 15 | - [Char Howland](https://github.com/cjhowland) 16 | - [Christian Bormann](https://github.com/c2bo) 17 | - [Dominic Wörner](https://github.com/domwoe) 18 | - [Daniel Bluhm](https://github.com/dbluhm) 19 | - [Kyle Den Hartog](https://github.com/kdenhartog) 20 | - [Artem Ivanov](https://github.com/artemkaaas) 21 | - [Renata Toktar](https://github.com/toktar) 22 | - [Alexander Shcherbakov](https://github.com/ashcherbakov) 23 | 24 | 25 | 26 | **Participate:** 27 | 28 | ~ [GitHub repo](https://github.com/hyperledger/indy-did-method) 29 | ~ [Commit history](https://github.com/hyperledger/indy-did-method/commits/main) 30 | 31 | ------------------------------------ 32 | -------------------------------------------------------------------------------- /spec/10_original_did_operations.md: -------------------------------------------------------------------------------- 1 | ## did:indy Operations 2 | 3 | There is a description of types and operations that are available in case of using [`indy-node`](https://github.com/hyperledger/indy-node) with [`indy-plenum`](https://github.com/hyperledger/indy-plenum) repositories. 4 | 5 | ### Creation 6 | 7 | Creation of a `did:indy` DID is performed by an authorized entity executing a `NYM` ledger transaction on a given Indy network. An Indy [[ref: NYM]] transaction includes an identifier (`dest`), an ED25519 verification key (`verkey`), an optional JSON item (`diddocContent`), and an optional NYM transaction `version`. The [[ref: NYM]] is written to a specific Indy network with a given `namespace`. The following validation MUST be performed prior to executing the transaction to create the DID: 8 | 9 | - Based on the configured authorization rules of the specific Indy ledger, the transaction may have to be signed by others, such as a Trustee or Endorser. If transaction is not authorized, the transaction MUST be rejected and an error returned to the client. 10 | 11 | - The Indy ledger MUST verify the relationship between the namespace identifier component of the DID and the initial public key (verkey) according to the NYM transaction `version` number described [below](#nym-transaction-version). If the relationship between the data elements fails verification, the transaction MUST be rejected and an error returned to the client. 12 | 13 | - The [[ref: NYM]] transaction requires that the transaction to be written is signed by the DID controller. The ledger MUST verify the signature using the [[ref: NYM]] `verkey`. If the signature can not be validated, the transaction MUST be rejected and an error returned to the client. 14 | 15 | - The Indy ledger MUST check that the data in the [[ref: NYM]] produces valid JSON and MUST do a limited DIDDoc validation check prior to writing the [[ref: NYM]] object to the ledger. Details of the assembly and verification are [below](#diddoc-assembly-and-verification). If the DIDDoc validation fails, the transaction MUST be rejected and an error returned to the client. 16 | 17 | Although the DIDDoc is returned from the DIDDoc assembly and verification process, the DIDDoc is not used further by the ledger. 18 | 19 | Once the validation checks are completed, the [[ref: NYM]] transaction is written to the Indy distributed ledger. If the [[ref: NYM]] write operation fails, an error is returned to the client. 20 | 21 | On successfully writing the transaction to the Indy distributed ledger a success status is returned to the client. 22 | 23 | #### NYM Transaction Version 24 | 25 | The NYM transaction `version` specifies the required level of validation of the relationship between the namespace identifier component of the DID and the initial public key (verkey). This field is optional, but if the NYM transaction `version` is provided, it must be set upon creation and cannot be updated. The accepted values are as follows: 26 | 27 | - 0 or NYM transaction `version` is not set: No validation of namespace identifier and initial verkey binding is performed. 28 | - 1: Validation is performed according to the `did:sov` method, in which the DID must be the first 16 bytes of the Verification Method public key. 29 | - 2: Validation is performed according to the `did:indy`, in which the namespace identifier component of the DID (last element) is derived from the initial public key of the DID, using the base58 encoding of the first 16 bytes of the SHA256 of the Verification Method public key (`did = Base58(Truncate_msb(16(SHA256(publicKey))))`). This DID is considered self-certifying. 30 | 31 | The NYM transaction `version` is distinct from the DID version described [below](#did-versions), which allows for resolution of DIDs at the specified `versionId` or `versionTime`. 32 | 33 | #### Backwards Compatibility 34 | 35 | Prior to `did:indy`, the unenforced convention in the [Indy SDK](https://github.com/hyperledger/indy-sdk) was to use the following to demonstrate the relationship between the DID and its initial verkey: `For an Ed25519 key: Convert into Base58char the first 16 bytes of the 256 bit public key (verkey).` If the `did:indy` approach to verifying the relationship between the DID and its initial verkey fail, a client resolving a DID MAY attempt to verify the relationship using the old Indy SDK convention. 36 | 37 | When using the old Indy SDK convention of using the first 16 bytes of the verkey, related convention allowed for the placement of a shortened verkey (prefixed with `~`) in the NYM `verkey` field, such that the full verkey was dynamically generated by combining the DID and shortened verkey. That convention is **NOT** used in "did:indy", but client resolvers using DIDs created prior to `did:indy` MUST detect and convert shortened verkeys to full verkeys as necessary. 38 | 39 | #### DIDDoc Assembly and Verification 40 | 41 | The DIDDoc returned when a `did:indy` DID is resolved is not directly stored in an Indy ledger document. Instead, the DIDDoc must be assembled from data elements in the Indy `NYM` object based on a series of steps. When a [[ref: NYM]] is created or updated the ledger MUST assemble the DIDDoc (following the steps) and validate the DIDDoc. As well, an Indy DID resolver will receive the [[ref: NYM]] from the ledger and the non-validation steps must be followed to assemble the DIDDoc for the resolved DID. 42 | 43 | The `diddocContent` item is stored directly in the ledger state and has a maximum size of 10 KiB (10 x 1024 bytes). If the `diddocContent` item contains a `@context` item, the resulting DIDDoc is considered JSON-LD. It is the responsibility of the content creator to ensure that it is valid JSON-LD. 44 | 45 | ##### DIDDoc Validation 46 | 47 | The following validation must be performed on a DIDDoc: 48 | 49 | - Content must not include `id` at root of the object 50 | - Content must not include any nested objects with an `id` ending in \#verkey 51 | 52 | This minimal validation allows for the evolution of the DIDDoc without the effort of updating deployments with each minor update. Instead, the writers and resolvers must ensure that the DIDDoc is valid beyond these basic requirements. 53 | 54 | #### DIDDoc Assembly Steps 55 | 56 | The following are the steps for assembling a DIDDoc from its inputs. 57 | 58 | 1. If the `verkey` is `null` the DID has been deactivated, and no DIDDoc is created. Assembly is complete; return a success status. 59 | 2. The Indy network instance `namespace`, the [[ref: NYM]] `dest` and the [[ref: NYM]] `verkey` items are merged into a text template to produce a base DIDDoc. 60 | 1. See the template in the [Base DIDDoc Template](#base-diddoc-template) section of this document. 61 | 2. If there is no `diddocContent` item in the [[ref: NYM]], assembly is complete; return the DIDDoc and a success status. 62 | 3. If the `diddocContent` item is included in the [[ref: NYM]], it is verified and merged into the DIDDoc. 63 | 1. The `diddocContent` item MUST NOT have an `id` item. If found, exit the assembly process, returning an error. 64 | 2. The `diddocContent` MUST NOT contain an item with the same `id` values as those from the [[ref: NYM]]-generated DIDDoc. If a matching `id` is found, exit and return an error. 65 | 3. If the `diddocContent` item contains `verificationMethod` and/or `authentication` items, these MUST be arrays. Merge the entries into the respective arrays of the DIDDoc. 66 | 4. Add the other items of the `diddocContent` to the DIDDoc. 67 | 4. The resulting DIDDoc text must be valid JSON. If not JSON, exit and return an error. 68 | 5. The resulting JSON must be a valid DIDDoc. Perform the [DIDDoc Validation](#diddoc-validation) process. If not a DIDDoc, exit and return an error. 69 | 6. Return the DIDDoc and a success status. 70 | 71 | The remainder of this section goes through examples of base DIDDoc template (step 2, above) that is created prior to processing the optional `diddocContent` item, and an example of processing a `diddocContent` item. 72 | 73 | ##### Base DIDDoc Template 74 | 75 | The base DIDDoc template is static text that forms a JSON structure. To transform a [[ref: NYM]] to its minimal DIDDoc, the Indy network instance's `namespace`, and the [[ref: NYM]] values `dest` and `verkey` are inserted into the template as indicated below. 76 | 77 | ```json 78 | { 79 | "id": "did:indy::", 80 | "verificationMethod": [{ 81 | "id": "did:indy::#verkey", 82 | "type": "Ed25519VerificationKey2018", 83 | "publicKeyBase58": "", 84 | "controller": "did:indy::" 85 | } 86 | ], 87 | "authentication": [ 88 | "did:indy::#verkey" 89 | ] 90 | } 91 | ``` 92 | 93 | Assuming values `sovrin` for the `namespace`, `123456` for `dest` and `789abc` for the `verkey` the resulting JSON DIDDoc would be: 94 | 95 | ::: example Base DIDDoc sovrin example 96 | ```json 97 | { 98 | "id": "did:indy:sovrin:123456", 99 | "verificationMethod": [{ 100 | "id": "did:indy:sovrin:123456#verkey", 101 | "type": "Ed25519VerificationKey2018", 102 | "publicKeyBase58": "789abc", 103 | "controller": "did:indy:sovrin:123456" 104 | } 105 | ], 106 | "authentication": [ 107 | "did:indy:sovrin:123456#verkey" 108 | ] 109 | } 110 | ``` 111 | ::: 112 | 113 | ##### Example Extended DIDDoc Item 114 | 115 | An example of a [[ref: NYM]]'s extended DIDDoc handling is provided below. In the example below, the `diddocContent` item adds a DIDcomm service endpoint to the resolved DIDDoc. Note that in the example, an `@context` item is included in the `diddocContent`, which causes the result DIDDoc to be a JSON-LD document, rather than plain JSON. 116 | 117 | ::: example Extended DIDDoc Item example 118 | ```json 119 | "diddocContent" : { 120 | "@context" : [ 121 | "https://www.w3.org/ns/did/v1", 122 | "https://identity.foundation/didcomm-messaging/service-endpoint/v1" 123 | ], 124 | "service": [ 125 | { 126 | "id": "did:indy:sovrin:123456#did-communication", 127 | "type": "did-communication", 128 | "priority": 0, 129 | "serviceEndpoint": "https://example.com", 130 | "recipientKeys": [ "#verkey" ], 131 | "routingKeys": [ ] 132 | } 133 | ] 134 | } 135 | ``` 136 | ::: 137 | 138 | Applying the DIDDoc assembly rules to the example above, the following assembled DIDDoc is produced (it is the responsibility of the content creator to ensure that the DIDDoc is valid JSON-LD): 139 | 140 | ::: example assembled Extended JSON-LD DIDDoc Item example 141 | ```json 142 | { 143 | "@context": [ 144 | "https://www.w3.org/ns/did/v1", 145 | "https://identity.foundation/didcomm-messaging/service-endpoint/v1" 146 | ], 147 | "id": "did:indy:sovrin:123456", 148 | "verificationMethod": [{ 149 | "id": "did:indy:sovrin:123456#verkey", 150 | "type": "Ed25519VerificationKey2018", 151 | "publicKeyBase58": "789abc", 152 | "controller": "did:indy:sovrin:123456" 153 | } 154 | ], 155 | "authentication": [ 156 | "did:indy:sovrin:123456#verkey" 157 | ], 158 | "service": [ 159 | { 160 | "id": "did:indy:sovrin:123456#did-communication", 161 | "type": "did-communication", 162 | "priority": 0, 163 | "serviceEndpoint": "https://example.com", 164 | "recipientKeys": [ "#verkey" ], 165 | "routingKeys": [ ] 166 | } 167 | ] 168 | } 169 | ``` 170 | ::: 171 | 172 | #### Key Agreement 173 | 174 | By default there is no key agreement section in an assembled DIDDoc. If the DID Controller wants a key agreement key in the DIDDoc, they must explicitly add it by including it in the `diddocContent`. For an ED25519 verification key, an X25519 key agreement key could be derived from the verkey (by the client), or a new key agreement key can be generated and used. 175 | 176 | #### The "endpoint" ATTRIB 177 | 178 | Prior to the definition of this DID Method, a convention on Indy ledgers to associate an endpoint to a [[ref: NYM]] involved adding an [[ref: ATTRIB]] ledger object with a `raw` value of contain the JSON for a name-value pair of `endpoint` and a URL endpoint, often an IP address. 179 | 180 | We strongly encourage anyone using the "ATTRIB `endpoint`" convention to update their [[ref: NYM]] on the ledger to use the `diddocContent` item as soon as possible, as the [[ref: ATTRIB]] is deprecated with the introduction of the `did:indy` DID Method. 181 | 182 | If a client retrieves a [[ref: NYM]] that has a `diddocContent` data element, the client should assume that the DID Controller has made the [[ref: ATTRIB]] (if any) obsolete and the client SHOULD NOT retrieve the [[ref: ATTRIB]] associated with the DID. 183 | 184 | If clients want to continue to retrieve and use the `endpoint` [[ref: ATTRIB]] transaction associated with a [[ref: NYM]], we recommend that the endpoint value (along with `namespace` and `dest`) be used as if the following was the `diddocContent` item in the [[ref: NYM]]. 185 | 186 | ``` json 187 | "diddocContent" : { 188 | "@context" : [ "https://identity.foundation/didcomm-messaging/service-endpoint/v1" ], 189 | "service": [ 190 | { 191 | "id": "did:indy:sovrin:123456#did-communication", 192 | "type": "did-communication", 193 | "priority": 0, 194 | "serviceEndpoint": "https://example.com", 195 | "recipientKeys": [ "#verkey" ], 196 | "routingKeys": [ ] 197 | } 198 | ] 199 | } 200 | ``` 201 | 202 | The DIDDoc produced by the [[ref: NYM]] and "endpoint" [[ref: ATTRIB]] would be created using the DIDDoc Assembly Rules and using the `diddocContent` from the [[ref: ATTRIB]] instead of the [[ref: NYM]] item. 203 | 204 | #### The "diddocContent" ATTRIB 205 | 206 | As described in previous sections, this DID Method introduces the optional `diddocContent` item in [[ref: NYM]] transactions, which is used in the [DIDDoc assembly process](#diddoc-assembly-and-verification). 207 | 208 | There may however be networks which use a version of Hyperledger Indy that doesn't support this field yet. In this case, when [creating](#creation) or [updating](#update) a `did:indy` DID, implementations MAY write the `diddocContent` to an [[ref: ATTRIB]] transaction with a `raw` value containing the JSON for a name-value pair with name `diddocContent`, instead of using the field in a [[ref: NYM]] transaction. 209 | 210 | Once such a network is upgraded to a version that supports the `diddocContent` item in the [[ref: NYM]], we strongly encourage anyone using the "ATTRIB `diddocContent`" convention to update their [[ref: NYM]] on the ledger to use the `diddocContent` item as soon as possible, analogous to the ["ATTRIB `endpoint`" convention](#the-endpoint-attrib). 211 | 212 | If a client retrieves a [[ref: NYM]] that has a `diddocContent` data element, the client should assume that the DID Controller has made the [[ref: ATTRIB]] (if any) obsolete and the client SHOULD NOT retrieve the [[ref: ATTRIB]] associated with the DID. 213 | 214 | Otherwise, the client SHOULD attempt to retrieve the `diddocContent` [[ref: ATTRIB]] transaction associated with a [[ref: NYM]] and, if present, treat it as if its `raw` value was the value of the `diddocContent` item in the [[ref: NYM]]. 215 | 216 | The following is an example ATTRIB transaction with a `raw` value containing `diddocContent`: 217 | 218 | ::: example Example ATTRIB transaction with a `raw` value containing `diddocContent` 219 | ```json 220 | { 221 | "txn": { 222 | "data": { 223 | "dest": "P8xKoMHo5tvaCBu9sg7qmE", 224 | "raw": "{\"diddocContent\":{\"@context\":[\"https://www.w3.org/ns/did/v1\",\"https://identity.foundation/didcomm-messaging/service-endpoint/v1\"],\"service\":[{\"id\":\"did:indy:sovrin:123456#did-communication\",\"type\":\"did-communication\",\"priority\":0,\"serviceEndpoint\":\"https://example.com\",\"recipientKeys\":[\"#verkey\"],\"routingKeys\":[]}]}}" 225 | }, 226 | "metadata": { 227 | "reqId": 1681588180411147000, 228 | "from": "P8xKoMHo5tvaCBu9sg7qmE", 229 | "digest": "38f422258c5f674f60e08274cf400a351dabfb3f5c80b59966f2889947bf3387", 230 | "payloadDigest": "0aa1a7d1de92da1056c7702dee10fba0cc7d13378f6cd11ec392da2d95c3e2fb" 231 | }, 232 | "protocolVersion": 2, 233 | "type": "100" 234 | }, 235 | "txnMetadata": { 236 | "seqNo": 807, 237 | "txnId": "P8xKoMHo5tvaCBu9sg7qmE:1:9bc57c8357576385437819bd163d4cd6dda6acb9a424033d50a646bc54438ef3", 238 | "txnTime": 1681588183 239 | } 240 | } 241 | ``` 242 | ::: 243 | 244 | ### Update 245 | 246 | Updating a DID using the Indy DID Method occurs when a `NYM` transaction is performed by the [[ref: NYM]]'s controller (the "owner" of the [[ref: NYM]]) using the same identifier (`dest`). The Indy ledger MUST validate the [[ref: NYM]] transaction prior to writing the [[ref: NYM]] to the ledger. 247 | 248 | When a [[ref: NYM]] is updated, the identifier (`dest`) for the [[ref: NYM]] does not change, but other values, including the `verkey` and `diddocContent`, may be changed. This means that (as expected) the DID itself does not change, but the DIDDoc returned by the DID may change. The NYM transaction `version` must not be updated. 249 | 250 | The following validation steps are performed prior to the update being written to the ledger: 251 | 252 | - Based on the configured authorization rules of the specific Indy ledger, the transaction may have to be signed by others, such as a Trustee or Endorser. If transaction is not authorized, the transaction MUST be rejected and an error returned to the client. 253 | 254 | - The [[ref: NYM]] transaction requires that the transaction to be written is signed by the DID controller using the existing `verkey`. The ledger MUST verify the DID controller's signature. If the DID controller's signature cannot be validated, the transaction MUST be rejected and an error returned to the client. 255 | 256 | - The Indy ledger MUST check that the data in the [[ref: NYM]] produces valid JSON and MUST do a limited DIDDoc validation check prior to writing the [[ref: NYM]] object to the ledger. Details of the assembly and verification are [here](#diddoc-assembly-and-verification). If the DIDDoc validation fails, the transaction MUST be rejected and an error returned to the client. 257 | 258 | - The Indy ledger checks that the NYM transaction `version` is not updated. If the `version` is updated, the transaction MUST be rejected and an error returned to the client. 259 | 260 | Although the DIDDoc is returned from the DIDDoc assembly and verification process, the DIDDoc is not used further by the ledger. 261 | 262 | Once the validation checks are completed, the [[ref: NYM]] update transaction is written to the Indy distributed ledger. If the [[ref: NYM]] write operation fails, an error MUST be returned to the client. 263 | 264 | On successfully writing the update transaction to the Indy distributed ledger a success status is returned to the client. 265 | 266 | ### Read 267 | 268 | Reading (resolving) a `did:indy` DID requires finding and connecting to the Indy ledger instance holding the DID, retrieving the [[ref: NYM]] associated with the DID, verifying the state proof for the returned [[ref: NYM]], and then assembling the DIDDoc. A client/resolver must perform the following steps to complete the process. 269 | 270 | 1. Given a `did:indy` DID, extract the `` component of the DID. 271 | 2. If the namespace (specific Indy instance) is known to the resolver and the resolver is connected to the ledger continue. If not: 272 | 1. To read the DIDDoc, the client must get the genesis file for the Indy instance and connect to the ledger. For example, the guidance in the [finding Indy ledgers](#finding-indy-ledgers) can be used to discover previously unknown Indy ledgers. 273 | 2. If the client cannot find the Indy network terminate the process and return a "Not Found" status to the caller. 274 | 3. If the client chooses not to connect to the Indy network terminate the process and return a "Not Authorized" status to the caller. 275 | 3. Once connected, the `GET_NYM` Indy request is used, passing in the `` component. 276 | 1. If resolving a prior version of the DID, a different call is used in at this point. See the [DID Versions](#did-versions) section of this document (below) for more details. 277 | 4. If the call fails, terminate the process and return a "Not Found" status to the caller. 278 | 5. If a [[ref: NYM]] is returned, use the state proof to verify the result. If the verification fails, terminate the process and return a "Not Found" status to the caller. 279 | 6. Use the DID `` component, the [[ref: NYM]] data items `dest`, `verkey`, and (optional) `diddocContent` to assemble the DIDDoc using the [DIDDoc assembly process](#diddoc-assembly-and-verification) defined earlier in this document. 280 | 1. Since the assembly validation was done by the ledger before writing the document, the process should be successful. 281 | 7. If the DIDDoc is empty (because the `verkey` is null) return a "Not Found" result, otherwise, return the DIDDoc. 282 | 283 | #### DID Versions 284 | 285 | In resolving a `did:indy` DID, the DID resolution query parameters `versionId` and `versionTime` may be used. When used, process to retrieve the [[ref: NYM]] from the ledger (step 3 above) is different. 286 | 287 | If the parameter `versionId` is used, the value must be an Indy ledger `seqno` for the requested [[ref: NYM]] on the queried Indy ledger. Instead of using the `GET_NYM` call, the Indy `GET_TXN` call is used, passing in the `seqno`. The result is checked that it is the [[ref: NYM]] matching the DID namespace identifier. If so the call is considered to have failed. Either way, the process continues at Step 4. 288 | 289 | If the parameter `versionTime` is used, the `GET_NYM` transaction is called with the appropriate `versionTime` timestamp as an additional parameter. The `versionTime` parameter MUST be a XML datetime, as defined in the [DID Core specification](https://www.w3.org/TR/did-core/#did-parameters), and will be converted into a POSIX timestamp by the resolver. The Indy ledger code tries to find the instance of the requested [[ref: NYM]] that was active at that time (using ledger transaction timestamps) and returns it (if found) or the call fails. Either way, the process continues at Step 4. 290 | 291 | ### Deactivate 292 | 293 | Deactivation of a `did:indy` DID is done by setting the [[ref: NYM]] verkey to null. Once done, the DIDDoc is not found (per 294 | the [DIDDoc Assembly Steps](#diddoc-assembly-steps)) and the [[ref: NYM]] cannot be updated again. 295 | -------------------------------------------------------------------------------- /spec/11_besu_did_operations.md: -------------------------------------------------------------------------------- 1 | ## did:indy:besu Operations 2 | 3 | There is a description of a storage format, types and operations that are available in case of using [`indy-besu`](https://github.com/hyperledger/indy-besu) repository. 4 | 5 | ### Storage format 6 | 7 | DID Records collection contains `DidRecord` structures consisting of DID Document and `DidMetadata` 8 | 9 | ``` 10 | struct DidRecord { 11 | bytes document; 12 | DidMetadata metadata; 13 | } 14 | struct DidMetadata { 15 | address owner; 16 | uint256 created; 17 | uint256 updated; 18 | uint256 versionId; 19 | bool deactivated; 20 | } 21 | ``` 22 | 23 | Example: 24 | 25 | ``` 26 | { 27 | "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { 28 | document: bytes(" 29 | { 30 | "@context": [ 31 | "https://www.w3.org/ns/did/v1", 32 | "https://w3id.org/security/suites/ed25519-2020/v1" 33 | ], 34 | "id": "did:indy:besu:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 35 | "verificationMethod": [{ 36 | "id": "did:indy:besu:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266#key-1", 37 | "type": "Ed25519VerificationKey2020", 38 | "controller": "did:indy:besu:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 39 | "publicKeyMultibase": "zH3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" 40 | }], 41 | "authentication": ["did:indy:besu:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266#key-1"], 42 | } 43 | "), 44 | metadata: { 45 | owner: 0x93917cadbace5dfce132b991732c6cda9bcc5b8a, 46 | created: 1234, 47 | updated: 1234, 48 | versionId: 1234, 49 | deactivated: false 50 | }, 51 | }, 52 | ... 53 | } 54 | ``` 55 | 56 | ### Types definition 57 | 58 | #### DidDocument 59 | 60 | DID Document must match to the [specification](https://www.w3.org/TR/did-core/). 61 | 62 | #### DID Document metadata 63 | 64 | Each DID Document MUST have a metadata section when a representation is produced. It can have the following properties: 65 | 66 | * owner (address): An address of DID owner 67 | * created (timestamp): Time of a block ordered a transaction for DID Doc creation 68 | * updated (timestamp): The updated field is null if an Update operation has never been performed on the DID document 69 | Time of a block ordered a transaction changed a DID Doc last time 70 | * versionId (number): Block number when DID was created or updated 71 | * deactivated (string): If DID has been deactivated, DID document metadata MUST include this property with the boolean 72 | value true. By default, this is set to false. 73 | 74 | ### DIDDoc Validation 75 | 76 | There is no specific validation of DID Document on the ledger side. It is implemented in Indy Besu VDR code instead. 77 | 78 | ### Transactions (Smart Contract's methods) 79 | 80 | Contract name: **IndyDidRegistry** 81 | 82 | #### Create DID 83 | 84 | * Method: `createDid` 85 | * Description: Transaction to create a new DID record (DID Document and corresponding DID Metadata) 86 | * Parameters: 87 | * `identity` - Address of DID owner 88 | * `document` - DID Document JSON as bytes 89 | * Restrictions: 90 | * DID must not exist 91 | * Valid DID must be provided 92 | * Sender must be equal to identity 93 | * Sender must have either TRUSTEE or ENDORSER or STEWARD role assigned 94 | * Format: 95 | ``` 96 | IndyDidRegistry.createDid( 97 | address identity, 98 | bytes document 99 | ) 100 | ``` 101 | * Example: 102 | ``` 103 | IndyDidRegistry.createDid( 104 | "0xa9b7df62c953c4c49deebea05d3c8fee1f47c1f6", 105 | "{ did document as json bytes }" 106 | ) 107 | ``` 108 | * Raised Event: 109 | * `DIDCreated(identity)` 110 | 111 | #### Update DID 112 | 113 | * Method: `updateDid` 114 | * Description: Transaction to update an existing DidDocStorage entry 115 | * Parameters: 116 | * `identity` - Address of DID owner 117 | * `document` - DID Document JSON as bytes 118 | * Restrictions: 119 | * DID must exist 120 | * DID must be active 121 | * Sender must be equal to identity 122 | * Sender must be either identity owner or have a TRUSTEE role assigned 123 | * Format: 124 | ``` 125 | IndyDidRegistry.updateDid( 126 | address identity, 127 | bytes calldata document 128 | ) 129 | ``` 130 | * Example: 131 | ``` 132 | IndyDidRegistry.updatedDid( 133 | "0xa9b7df62c953c4c49deebea05d3c8fee1f47c1f6" 134 | "{ did document as json bytes }" 135 | ) 136 | ``` 137 | * Raised Event: 138 | * `DIDUpdated(identity)` 139 | 140 | #### Deactivate DID 141 | 142 | * Method: `deactivateDid` 143 | * Description: Transaction to deactivate an existing DID 144 | * Parameters: 145 | * `identity` - Address of DID owner 146 | * Restrictions: 147 | * DID must exist 148 | * DID must be active 149 | * Sender must be equal to identity 150 | * Sender must be either identity owner or have a TRUSTEE role assigned 151 | * Format: 152 | ``` 153 | IndyDidRegistry.deactivateDid( 154 | address identity 155 | ) 156 | ``` 157 | * Example: 158 | ``` 159 | IndyDidRegistry.deactivateDid( 160 | "0xa9b7df62c953c4c49deebea05d3c8fee1f47c1f6" 161 | ) 162 | ``` 163 | * Raised Event: 164 | * `DIDDeactivated(identity)` 165 | 166 | #### Endorsement flow 167 | 168 | Not all identity owners may have permissions for writing transactions on the ledger. 169 | 170 | For endorsement flow `IndyDidRegistry` contains methods: `createDidSigned`, `updateDidSigned` and `deactivateDidSigned`. They duplicate `createDid`, `updateDid` and `deactivateDid` with additional `signature` parameter. 171 | `Signature` is EcDSA signature by the DID author's Ethereum identity account keys. 172 | 173 | #### Resolve DID Document with Meta 174 | 175 | * Method: `resolveDid` 176 | * Description: Transaction to resolve DidDocStorage entry (DID Document and corresponding DID Doc Metadata) 177 | * Parameters: 178 | * `identity` - Address of the DID identity to be resolved 179 | * Restrictions: 180 | * DID must exist 181 | * Format: 182 | ``` 183 | IndyDidRegistry.resolveDid( 184 | address identity, 185 | ) returns (DidRecord didRecord) 186 | ``` 187 | * Example: 188 | ``` 189 | IndyDidRegistry.resolveDid( 190 | "0xa9b7df62c953c4c49deebea05d3c8fee1f47c1f6" 191 | ) 192 | ``` 193 | * Raised Event: `None` -------------------------------------------------------------------------------- /spec/12_did_indy_did_component_syntax.md: -------------------------------------------------------------------------------- 1 | ## `did:indy` DID Component Syntax 2 | 3 | The following sections provide the syntax and ABNF for the two variable components of a `did:indy` DID, the namespace and namespace identifier. 4 | 5 | ### `did:indy` DID Namespace Syntax 6 | 7 | The `did:indy` DID Namespace component MUST include a primary, human-friendly name of the Indy network instance, MAY include an optional ":" separator and subspace, human-friendly name, and MUST include a trailing ":" separator. The subspace name is used to identify an Indy instance related to the primary instance, such as the primary network's test or development instance. The ABNF for the namespace component is: 8 | 9 | namespace = namestring (":" namestring) ":" 10 | namestring = lowercase *(lowercase / DIGIT / "_" / "-") 11 | lowercase = %x61-7A ; a-z 12 | 13 | The namespace is set by the operator of the network. Although that could lead to namespace collisions, our 14 | [assumption about the expected number of Indy instances](#assumption-number-of-indy-instances) (low 100s at the most) eliminates that as a concern. 15 | 16 | ### Original `did:indy` DID Namespace Identifier Syntax 17 | 18 | The namespace identifer is an identifier within the namespace of a Hyperledger Indy network that is unique for that namespace. 19 | 20 | The namespace identifier (NSID) is defined by the following ABNF: 21 | 22 | NSIDstring = 21*22(base58char) 23 | base58char = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" / "A" / "B" / "C" 24 | / "D" / "E" / "F" / "G" / "H" / "J" / "K" / "L" / "M" / "N" / "P" / "Q" 25 | / "R" / "S" / "T" / "U" / "V" / "W" / "X" / "Y" / "Z" / "a" / "b" / "c" 26 | / "d" / "e" / "f" / "g" / "h" / "i" / "j" / "k" / "m" / "n" / "o" / "p" 27 | / "q" / "r" / "s" / "t" / "u" / "v" / "w" / "x" / "y" / "z" 28 | 29 | The `NSIDString` is base58 encoded using the Bitcoin/IPFS alphabets of a 16-byte uuid. The encoding uses most alphas and digits, omitting 0 / O / I / l to avoid readability problems. This gives a NSID length of either 21 or 22 characters, and it means that identifiers are case-sensitive and may not be case-normalized, even though the prefix is always lower-case. 30 | 31 | The namespace identifier MUST be derived from the initial `verkey` for the DID, as outlined in the [Creation](#creation) section of this document. 32 | 33 | ### `did:indy:besu` DID Namespace Identifier Syntax 34 | 35 | The namespace identifier is an identifier within the namespace of a Hyperledger Indy network that is unique for that namespace. 36 | 37 | ethereum-address-identifier = "0x" 40*HEXDIG 38 | 39 | In this case, the identifier is the Ethereum address. Since when creating a DID document using a signature of a public key of this Ethereum account, the DID Document is self-trusted. 40 | -------------------------------------------------------------------------------- /spec/13_json_or_json-ld.md: -------------------------------------------------------------------------------- 1 | ## JSON or JSON-LD 2 | 3 | The choice of whether a DID resolves to a JSON or JSON-LD document is up to the DID Controller. 4 | 5 | - If the original Indy [[ref: NYM]] object on the ledger for the DID has a `diddocContent` data item, and that data item contains an `@context` item, the DIDDoc will be JSON-LD. 6 | 7 | - In case of using Indy Besu network and `context` has been defined for DID Document object, the DIDDoc will be JSON-LD. 8 | 9 | Otherwise, the resolved DIDDoc will be JSON. 10 | 11 | The Indy ledger does not validate the JSON-LD on writing a DID object to the ledger. It is the responsibility of the DID Controller to ensure that a JSON-LD DIDDoc is valid JSON-LD. 12 | -------------------------------------------------------------------------------- /spec/14_privacy_considerations.md: -------------------------------------------------------------------------------- 1 | ## Privacy Considerations 2 | 3 | Given that Indy is a publicly readable, immutable ledger, no personally identifiable information, including DIDs where a person is the DID Subject, should be placed on the network. As this DID method does not offer yet any means to delete or deactivate personal information (e.g. in the sense of GDPR), it is important to enforce these rules by organizational means, for example through an Endorser Transaction Agreement or other contractual agreements. 4 | 5 | The further privacy properties are stated according to [Section 5 of RFC6973](https://tools.ietf.org/html/rfc6973#section-5). 6 | 7 | ### Surveillance 8 | 9 | The DIDs and their resolved DID Documents are public readable and therefore the content and the changes of the data are inherently suspectable to surveillance. 10 | Furthermore, authors of read and write requests can be surveilled by their communication to the ledger nodes. Clients sending write requests can be observed and identified by their author DIDs and signatures. However, read requests are unsigned and only need to be sent to one node, therefore offering choice and better protection from surveillance for the client. 11 | 12 | ### Stored Data Compromise 13 | 14 | The compromise of stored data on the ledger is prevented by the distributed, signed and consensus-based storage of the data. The stored data of an individual ledger node shall be protected by implementing best practice in securing the IT infrastructure, like ISO27001 and Information Security Management systems (ISMS). 15 | 16 | ### Unsolicited Traffic 17 | 18 | DID Documents can be resolved from a DID, however the DID subject can choose to include or exclude service endpoints that expose itself to unsolicited traffic. The nodes of the ledger itself are exposed to any unwanted traffic as explained in the Denial-of-Service section. 19 | 20 | ### Misattribution 21 | 22 | DIDs of NYM and DID Document transaction `version` 2 are self-certifying and immutable, the control flow of the ledger nodes prevents any misattribution given that it is implemented correctly. It is the responsibility the creator of the DIDs to elect to use the self-certification feature. 23 | 24 | ### Correlation 25 | 26 | The Hyperledger Indy ecosystem with Anoncreds 1 was designed to prevent correlation by design. No DIDs nor any data or credentials of natural persons are stored on the ledger, the revocation system guarantees a high degree of anonymity. 27 | However, DIDs on the ledger, used by organizations, enable correlation by the pseudonymous DID itself or through data like service endpoints from the resolved DID Document as described in the DID-Core specification. Ledger nodes are prohibited to collect any metadata of (read) requests to the ledger to prevent correlation. 28 | 29 | ### Identification 30 | 31 | The Hyperledger Indy ecosystem prevents identification of natural persons as they do not have a DID on the ledger. Identification of DIDs through the data of the resolved DID Document is possible and usually desired as issuing or verifying organizations want to authentically disclose their identity. 32 | 33 | ### Secondary Use 34 | 35 | As all data written to the ledger is inherently public, clients sending write requests should be aware of possible secondary use and cautiously decide whether data appropriate data is published. No personal data shall be send to the ledger. 36 | 37 | ### Disclosure 38 | 39 | Disclosure of data send to the ledger is not an issue, as written data is public anyway. Ledger nodes are prohibited to collect any metadata of (read) requests to the ledger to prevent disclosure. 40 | 41 | ### Exclusion 42 | 43 | Any read request to the ledger nodes in unauthorized preventing exclusion to the ledger data. 44 | -------------------------------------------------------------------------------- /spec/15_security_considerations.md: -------------------------------------------------------------------------------- 1 | ## Security Considerations for Indy Node 2 | 3 | Hyperledger Indy Node is a public, permissioned distributed ledger that uses RBFT to establish a consensus between upfront well-authenticated nodes. The security mechanisms by indy-node and indy-plenum guarantee the correct processing of requests and transactions according to the rules, which are themselves part of the consensus on the ledger. In particular, this enables the creation and update of schemas, credential definitions and DIDs by their owners by authenticating with the corresponding public keys stored on the ledger. 4 | 5 | Hyperledger uses [CurveZMQ](http://curvezmq.org/page:read-the-docs#toc3) to secure the communication between the ledger nodes and the clients as well as between the ledger nodes for the consensus. CurveZMQ is an adaptation of the low-level, TCP-based ZMQ communication protocol using the [CurveCP](http://curvecp.org/) to protect the communication layer. CurveCP was designed by Daniel J. Bernstein and uses the 256-bit Curve25519 and derived short-term session keys. 6 | 7 | Hyperledger Indy uses a genesis file to securely authenticate the (initial) trusted nodes. This genesis file specifically includes the IP addresses and the long-term keys of the ledger nodes, as well as the DIDs and verification keys of the Trustees and Stewards. The ledger is initially started in a launch ceremony, where all th ledger nodes simultaneously start the indy-node software and ensure the correct startup. The information in the genesis file is sufficient for each client to securely connect to the ledger and gain trust in the transactions provided by the nodes. 8 | 9 | The following sections describe how the `did:indy` DID Method adheres to the security considerations outlined in the [DID Core 1.0 specification](https://w3c.github.io/did-core) and in accordance with RFC3552. 10 | 11 | ### Eavesdropping 12 | 13 | The CurveZMQ protocol provides confidentiality and integrity by encrypting and authenticating each packet. 14 | 15 | ### Replay 16 | 17 | The CurveZQM protocol provides protection against replay attacks by including a unique nonce. An attacker cannot replay any packet except a Hello packet, which has no impact on the server or client. Any other replayed packet will be discarded by its recipient. 18 | Additionally, the replies for read requests include a timestamp of the state proof of the consensus, therefore the client can check the freshness of the received data. 19 | 20 | ### Message Insertion and Modification 21 | 22 | The CurveZMQ protocol prevents malicious message insertion as all packets are authenticated and therefore protected against any form of forgery. 23 | For write requests, the client signs the sensitive request content with his verification key to authenticate and therefore protect the data against malicious manipulation. 24 | Additionally, the consensus prevents forgery of the data returned from the ledger. The stateProof is a multi-signed root hash of a merkle tree that includes all the ledger objects and is part of the node's reply. 25 | 26 | ### Man-In-The-Middle 27 | 28 | The client knows the long-term public keys of the ledger nodes from the trusted genesis file. The CurveZMQ protocol protects a Man-In-The-Middle that tries to impersonate the server (ledger node). CurveCP does not protect server from an attacker impersonating the client, as no client authentication is performed on the transport layer. However, the client authenticates on the application layer with by signatures with verification key for the sensitive write requests. 29 | Man-in-the-Middle attacks for the inter-node communication is also impossible, as both parties know each others long term public key. 30 | 31 | ### Deletion 32 | 33 | Deletion of individual messages is not a security risk, as all both every request is answered with a signed response, therefore the lack of response will let the requesting party acknowledge the possible deletion of its message. 34 | 35 | ### Denial of Service 36 | 37 | Several measures for protection against Denial of Service are taken into account for the indy ledger ecosystem: 38 | 39 | - CurveCP uses high-speed high-security elliptic-curve cryptography so that a typical CPU can perform public-key operations faster than a typical Internet connection can ask for those operations. The server does not allocate memory until a client sends the Initiate packet. 40 | - Separate network interfaces to prevent loss of node-to-node communications are advised 41 | - For read requests communication to only one ledger node is necessary to get authentic ledger data as indy uses multi-signed state proofs 42 | 43 | ### Storage or Network amplification 44 | 45 | Amplification attacks to exhaust the storage of the ledger nodes can be easily mitigated, as only permissioned DIDs have write access and in case of massive malicious requests, these endorsement rights can be revoked. 46 | 47 | Amplification attacks to exhaust the network bandwidth are protected to measures similar to section Denial of Service. Additionally, read requests are cheap to process for the ledger node, as the multi-signed state proof is applicable to all requested ledger objects and no new signatures need be calculated except for transport layer security. CurveZMQ is also designed to ensure high efficiency and availability itself. 48 | 49 | ## Security Considerations for Indy Besu 50 | 51 | Hyperledger Indy Besu implementation is based on Hyperledger Besu and Ethereum and inherit their security policy. 52 | 53 | ### Consensus Algorithms 54 | 55 | The Hyperledger Besu implementation utilizes Proof of Authority consensus mechanism from Indy Besu. 56 | 57 | QBFT: QBFT is the recommended consensus protocol for private networks. In QBFT networks, validators validate transactions and blocks, taking turns to create the next block. Before a block can be added to the chain, a super-majority of validators must sign off on it (greater than or equal to ⅔). 58 | 59 | ### Storage 60 | 61 | Hyperledger Besu utilizes a RocksDB key-value database for storing chain data locally. This data is categorized into: 62 | - Blockchain: Blockchain data is composed of block headers that form the “chain” of data that is used to cryptographically verify blockchain state; block bodies that contain the list of ordered transactions included in each block; and transaction receipts that contain metadata related to transaction execution including transaction logs. 63 | - World State: Each block header references a world state via a stateRoot hash, mapping addresses to accounts. Externally owned accounts have ether balances, while smart contract accounts also contain code and storage. 64 | - Bonsai: Bonsai storage is a novel paradigm for storing Ethereum state, built specifically for keeping Ethereum Mainnet storage requirements low. Bonsai organizes states into a new structure, to provide implicit pruning and low requirements. More details [here](https://besu.hyperledger.org/en/latest/public-networks/concepts/data-storage-formats/#bonsai-tries). 65 | - Forest: A traditional trie structure, suitable for private networks and archival nodes. 66 | 67 | ## Residual Risks 68 | 69 | Residual risks for Hyperledger Indy include: 70 | 71 | - compromise in the used cryptographic primitives 72 | - indy-node/indy-plenum: BLS-Signature, X25519, Ed25519; 73 | - indy-besu: secp256k1; 74 | - implementation bugs in the indy-node, indy-plenum or indy-besu software, especially as all nodes use the same software package 75 | - external libraries 76 | 77 | ### Integrity protection and update authentication for method operations and write authorization 78 | 79 | For all write operations that alter the ledger data(Create, Update, Deactivate) the appropriate signatures are needed, where the DIDs and Verification Method public keys (`verkey`s) of the signatories must be one the ledger. The client signs the body of the [write request](https://github.com/hyperledger/indy-node/blob/master/docs/source/requests.md#reply-structure-for-write-requests) (`txn`) for indy-node and [EIP1559 transactions](https://besu.hyperledger.org/23.4.0/public-networks/concepts/transactions/types#eip1559-transactions) for indy-besu, depending on the configured ledger rules, some transactions might require signatures from multiple DIDs. The Indy ledger processes the signed write request and the software enforces the integrity protection by checking the validity of the given signatures and their authorization. No authentication or authorization is required for read requests. 80 | 81 | As indy-node currently only provides a single `verkey` for authentication, this exposes a risk of loss of control if the private key for the DID Controller is lost. 82 | 83 | As indy-besu currently only provides a single `secp256k1` signature check for authentication, this exposes a risk of loss of control if the private key for the DID Controller is lost. 84 | 85 | ### Uniqueness of DIDs 86 | 87 | For indy-node/indy-plenum DIDs on the Indy ledger have the option to be self-certifying (NYM transaction `version` 2), derived from the initial `verkey` of an Ed25519 key pair, which makes them extremely likely to be unique. Any attempt to write the same DID would only work if the signature matched (e.g. if the seed to create the DID had been lost so the literal same DID was attempted to be written), which would result in no change to the ledger, and goes against assumption of the DID Controller not protecting their seed/private key. If the signatures do not match in the case of a collision of DIDs, the NYM transaction would be rejected. DIDs of NYM transaction `version` 1 are also very likely to be unique, but a collision is possible. DIDs that have no validation of namespace identifier and initial verkey binding (NYM transaction `version` 0 or without a NYM transaction `version`) have no guarantees on uniqueness. While DIDs can have different levels of validation as determined by the creator (see [DID Creation](#creation) section for details), self-certification of DIDs will be enforced in the future. 88 | 89 | For indy-besu DIDs on the Indy ledger are always self-certifying because an identifier forms from an Ethereum account public key. 90 | 91 | ### Endpoint authentication 92 | 93 | No endpoint authentication is used other than the trusted node keys from the genesis files. 94 | 95 | ### Protection of data 96 | 97 | Transaction data on the ledger stored by the nodes is protected for integrity by maintaining cryptographically signed root hashes of the [merkle tree](https://github.com/hyperledger/indy-plenum/blob/master/docs/source/storage.md) for indy-plenum and [Ethereum merkle tree](https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/) for indy-besu, that includes all transactions of the ledger. Data is not protected for integrity as all data is inherently public anyway. 98 | 99 | ### Protection of Key Material 100 | 101 | The private keys associated with the public keys written to the ledger must be kept secret. Each DID Owner is responsible to store and use his private keys in a secure and sensitive way. 102 | 103 | The ledger nodes are operating with the Validator BLS key, which is stored in software on the node. This private key and the seed used for private key generation is sensitive and can be rotated by using the Steward role of the node's operator. The Trustees and Stewards `verkey`s are highly sensitive and must be securely stored (ideally with Two-Factor-Authentication or in hardware) separate from the ledger. Custom ledger rules that require multiple Trustee and/or Steward signatures to change important configuration data of the ledger are strongly advised. 104 | 105 | ### Peer-to-Peer Computing resources 106 | 107 | The RBFT algorithm in indy-plenum was initially designed as a robust Consensus protocol that limits the effect of malicious nodes. However, this requires some computations that limits the efficiency and scalability of the consensus. It ist strongly advised to not increase the number of validator nodes above 25. Hyperledger Indy was designed with very limited number of write requests, but very efficient read requests (only one node needs to be queried). If scalability issues still arise, the concept of [Observer nodes](https://github.com/hyperledger/indy-plenum/blob/master/design/observers.md) can be implemented. 108 | 109 | For networking Hyperledger Indy Besu uses Hyperledger Besu. It implements Ethereum's devp2p network protocols for inter-client communication, along with an additional sub-protocol for IBFT2 consensus: 110 | - Discovery: A UDP-based protocol for finding peers on the network 111 | - RLPx: A TCP-based protocol for communication between peers via various “sub-protocols”: 112 | - ETH Sub-protocol (Ethereum Wire Protocol): Used to synchronize blockchain state across the network and propagate new transactions. 113 | - IBF Sub-protocol: Used by IBFT2 consensus protocol to facilitate consensus decisions. 114 | 115 | ### New authentication methods for indy-node/indy-plenum 116 | 117 | New authentication methods can be added to the DID Document by adding them to the `didDocContent` field. However, the only authentication method applicable to the ownership of Indy-related DID (NYM), is the original `verkey`. 118 | -------------------------------------------------------------------------------- /spec/16_future_directions.md: -------------------------------------------------------------------------------- 1 | ## Future Directions 2 | 3 | ### Multiple Signature NYMs 4 | 5 | While not part of this version of the Indy DID Method specification, the group defining the specification recommends that the Indy `NYM` be extended to support multiple signature scenarios, where the [[ref: NYM]] can be updated to include multiple verification keys, and ledger rules defined for authentication of update transactions, such as M signatures of N total signatures are required. Such a change would be reflected in the `verificationMethod` and `authentication` items in the generated DIDDoc. 6 | 7 | ### Recovery Mechanism 8 | 9 | While not part of this version of the Indy DID Method specification, the group defining the specification recommends that the Indy `NYM` be extended to support a recovery mechanism, such as defined in [KERI](https://keri.one). To summarize, a recovery mechanism involves the controller creating not only a verification key pair, but also a recovery public/private key pair during create and update operations. The recovery key is added in some form to (in the case of Indy) the [[ref: NYM]]. With a recovery mechanism in place, if only the active key becomes compromised, only the controller can rotate to the recovery key. Assuming the recovery private key is maintained in an even more secure location than the active private key, recovery should always be possible. Without a recovery mechanism, a compromised active key can be used to rotate the verification key and take control of the [[ref: NYM]] (and DID) from the controller. 10 | 11 | ### KERI Support 12 | 13 | Some consideration was given in designing the initial version of the `did:indy` DID method to including support for supporting KERI identifiers as part of the DID method. At the time of completing the initial version of the spec there was not a clear definition of what "support" would mean, and the concept was moved to this section of the specification. 14 | 15 | ### Cross Registering Indy Ledgers 16 | 17 | Some consideration was given to the idea of allowing network discovery by registering network configuration data (such as Indy genesis files) for a ledger on other ledgers. With such a capability with Indy, connecting to one ledger would allow the discovery of all other Indy ledgers registered on that ledger. The idea was partially explored in [this document](https://docs.google.com/document/d/1qLCaUiPtFZVNVUkAcLOhkPDPFs-ealTQmmy4HvYYhXQ/edit?usp=sharing), but the design was not completed in time for inclusion. 18 | 19 | ### Other Signature Schemes 20 | 21 | Indy implicitly uses the ED25519 Signature scheme for the `verkey`. We recommend that the [[ref: NYM]]s be evolved to explicitly state the signature scheme so that other signature key schemes can be used on Indy networks. 22 | -------------------------------------------------------------------------------- /spec/1_about.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | The Indy DID method specification conforms to the requirements in the DID specification currently published by the W3C Credentials Community Group. For more information about DIDs and DID method specifications, please see the DID Primer and DID Spec. 4 | -------------------------------------------------------------------------------- /spec/2_abstract.md: -------------------------------------------------------------------------------- 1 | ## Abstract 2 | 3 | Indy is a public ledger designed specifically and only for privacy-preserving self-sovereign identity. A Hyperledger Indy ledger is designed specifically to enable the use of verifiable credentials, allowing credential issuers to publish data necessary for issuing verifiable credentials and constructing presentations from those verifiable credentials. This specification covers how DIDs on an Indy ledger are managed and the operations for creating, reading, updating, and deleting DIDs. 4 | -------------------------------------------------------------------------------- /spec/3_indy_ledger_object_glossary.md: -------------------------------------------------------------------------------- 1 | ## Indy Ledger Objects: Glossary 2 | 3 | Instances of Hyperledger Indy networks persist different kind of (internal) data objects in the ledger. The following section describes those objects. 4 | 5 | [[def: DID Document]] 6 | 7 | ~ A DID document is an entity containing a distributed identifier and properties of the owner of this `did`. 8 | 9 | ~ A DID Document conform to the [Decentralized Identifiers (DIDs) Core specification](https://https://www.w3.org/TR/did-core/). 10 | 11 | [[def: NYM]] 12 | 13 | ~ A NYM is a legacy Indy entity 14 | 15 | ~ A NYM (short for "Verinym") is associated with the Legal Identity of an Identity Owner and is a Hyperledger Indy specific term for a data object, which holds DID data of one concrete identity returned during DID resolution. While a NYM can be [read](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-nym) from a Hyperledger Indy Node by any client, a NYM can only be [written](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#nym) to a Hyperledger Indy network as long as the writing entity possess the proper permissions. 16 | 17 | ~ A NYM object itself does not conform to the [Decentralized Identifiers (DIDs) Core specification](https://https://www.w3.org/TR/did-core/) scheme but rather includes all DID related data of a single identity and therefore its resolution into a DID document does. Therefore writing a NYM to a Hyperledger Indy instance basically results in writing a DID to the ledger. The author of a NYM write transaction is then the owner of the NYM respectively and its embedded DID. 18 | 19 | ~ When reading NYM transaction from the ledger, clients transform / extract the NYM data into a DID document. Often the terms "NYM" and "DID" are used synonymously, although a "NYM" is "just" Hyperledger Indy's specific way of storing DIDs into the ledger. 20 | 21 | [[def: ATTRIB]] - **Deprecated** 22 | 23 | ~ A Hyperledger Indy ATTRIB (short for "attribute") object extends a specific DID (also known as [[ref: NYM]]) of its owner with further information (attributes) and can be [read](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-attrib) from a Hyperledger Indy Node by any client. An ATTRIB object can only be [written](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#attrib) to a Hyperledger Indy network by an owner of the DID on that network. 24 | 25 | ~ An ATTRIB is a legacy entity. The use of ATTRIB is **deprecated** with the introduction of the `did:indy` DID Method. The only common use of ATTRIBs in Hyperledger Indy prior to `did:indy` was to define DIDDoc service endpoints for a DID. Since with `did:indy` such a service endpoint can be added directly to the DID (along with any other DIDDoc data) there is no need to continue the use of the older ATTRIB `endpoint` convention. While a Hyperledger Indy client (such as [[ref: Indy VDR]]) MAY continue to try to resolve an `endpoint` ATTRIB when there is no DIDDoc content in a resolved DID, the ongoing practice of using an ATTRIB for that or any other purpose is discouraged. 26 | 27 | ~ The `did:indy` method introduces legacy support for retrieving the service endpoint of a past version of an attribute by `versionId` or `versionTime`, using a process similar to resolving [DID Versions](\https://hyperledger.github.io/indy-did-method/#did-versions) by these parameters. 28 | 29 | [[def: SCHEMA]] 30 | 31 | ~ A SCHEMA object is a template that defines a set of attribute (names) which are going to be used by issuers for issuance of [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) within a Hyperledger Indy network. SCHEMAs have a name, version and can be [written](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/transactions.html#schema) to the ledger by any entity with proper permissions. Schemas can be [read](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-schema) from a Hyperledger Indy Node by any client. 32 | 33 | ~ SCHEMAs define the list of attribute (names) of issued credentials based on a [[ref: CRED_DEF]] (see below). 34 | 35 | [[def: CRED_DEF]] 36 | 37 | ~ A CRED_DEF (short for "credential definition") object contains data required for credential issuance as well as 38 | credential validation and can be [read](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-claim-def) by any Hyperledger Indy client. A CRED_DEF object references a [[ref: SCHEMA]], references a DID of the issuer and can be [written](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#claim-def) by any issuer who intends to issue credentials based on that specific [[ref: SCHEMA]] to the ledger and has the proper permissions in doing so. A public key of the issuer is included within the CRED_DEF which allows validation of the credentials signed by the issuer's private key. When credentials are issued by using the issuers CRED_DEF, the attribute (names) of the [[ref: SCHEMA]] have to be used. 39 | 40 | ~ Revokable Verifiable Credentials require CRED_DEFs which also reference a [[ref: REV_REG_DEF]] (see below). 41 | 42 | [[def: CLAIM_DEF]] - **Deprecated** 43 | 44 | ~ The deprecated term CLAIM_DEF is sometimes used to describe a CRED_DEF, particularly in existing Hyperledger Indy code. 45 | 46 | [[def: REV_REG_DEF]] 47 | 48 | ~ A REV_REG_DEF object (short for "revocation registry definition") contains information required for verifiers in order to enable them to verify whether a (revokable) verifiable credential has been revoked by the issuer since issuance. 49 | 50 | ~ REV_REG_DEFs are only needed for revokable verifiable credentials and are most commonly [written](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#claim-def) to the ledger by the owner of a [[ref: CRED_DEF]] immediately after the [[ref: CRED_DEF]] has been written. They can be [read](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-attrib) from a Hyperledger Indy Node by any client and are updated in case of the revocation of a credential, which is based on the used [[ref: CRED_DEF]]. 51 | 52 | ~ Further details about Hyperledger Indy's revocation process can be found [here](https://hyperledger-indy.readthedocs.io/projects/hipe/en/latest/text/0011-cred-revocation/README.html). 53 | 54 | [[def: REV_REG_ENTRY]] 55 | 56 | ~ A REV_REG_ENTRY object (short for "revocation registry entry") marks the current status of one or more revokable verifiable credentials ("revoked" or "not revoked") in the ledger in a privacy preserving manner. A REV_REG_ENTRY is [written](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#revoc-reg-entry) by the owner of a REV_REG_DEF respectively the issuer of the credential(s) based on a [[ref: CRED_DEF]] and its REV_REG_DEF. 57 | 58 | ~ Any REV_REG_ENTRY condensed with further required information can be [read](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-revoc-reg-delta) by any Hyperledger Indy client. 59 | 60 | ~ Further details about Hyperledger Indy's revocation process can be found [here](https://hyperledger-indy.readthedocs.io/projects/hipe/en/latest/text/0011-cred-revocation/README.html). 61 | 62 | [[def: Indy VDR]] 63 | 64 | ~ Hyperledger Indy VDR (for "Verifiable Data Registry") is an open source implementation of an Indy client/resolver for both DIDs and other Indy objects. 65 | The repository is called indy-vdr and can be found [here](https://github.com/hyperledger/indy-vdr). 66 | 67 | [[def: Indy Besu VDR]] 68 | 69 | ~ Hyperledger Indy Besu VDR (for "Verifiable Data Registry") is an open source implementation of an Indy Besu client/resolver for both DIDs and other Indy objects. The repository is called indy-besu contains Indy Besu VDR [here](https://github.com/hyperledger/indy-besu/tree/main/vdr). 70 | -------------------------------------------------------------------------------- /spec/4_differences_from_did_sov.md: -------------------------------------------------------------------------------- 1 | ## Differences between `did:sov`, `did:indy` and `did:indy:besu` 2 | 3 | Early instances of Indy Networks used the `did:sov` DID Method. The following summarizes the differences between that `did:sov` and `did:indy`. 4 | 5 | - A `did:indy` DID includes a namespace component that enables resolving a DID to a specific instance of an Indy network (e.g. Sovrin, IDUnion, etc.). 6 | - Identifiers for Indy ledger objects other than [[ref: NYM]]s are adjusted to contain a namespace component. 7 | - `did:indy` DID validation is determined by original NYM transaction `version`, as described in the [DID Creation](#nym-transaction-version) section. These restrictions MUST be enforced by the ledger. 8 | - `did:indy:besu` DID validation placed in [[def: Indy Besu VDR]]. 9 | - For original indy the specification includes rules for transforming a [[ref: NYM]] into a DIDDoc that meets the DID Core Specification. 10 | - An optional [[ref: NYM]] data item allows entities to extend the DIDDoc returned from a [[ref: NYM]] in arbitrary ways. 11 | - The controller can decide whether the DIDDoc will be JSON or JSON-LD. 12 | - Before writing a [[ref: NYM]] to the ledger, the [[ref: NYM]] content is verified to ensure that transforming the [[ref: NYM]] to a DIDDoc produces a valid JSON and may include DIDDoc validity checking. 13 | - The transformation of a read [[ref: NYM]] to a DIDDoc is left to the client of an Indy ledger. 14 | - For `did:indy:besu` transforming of ledger data into a DIDDoc is not needed. Objects are stored already in the [Decentralized Identifiers (DIDs) Core specification](https://https://www.w3.org/TR/did-core/) compatible format. 15 | - A convention for storing Indy network instance config ("genesis") files in a Hyperledger Indy project GitHub repository ("indy-did-networks") is introduced. 16 | 17 | ### Compatibility `did:indy:besu` with other identifiers 18 | 19 | The idea is using of a basic mapping between other DIDs identifiers and Ethereum accounts instead of introducing a new DID method. 20 | 21 | * There is a mapping structure `legacyIdentifier => ethereumAccount` for storing `did:indy` and `did:sov` DID identifiers to the corresponding account address 22 | * Note, that user must pass signature over identifier to prove ownership 23 | * On migration, DID owners willing to preserve resolving of legacy formatted DIDs and id's must add mapping between legacy identifier and Ethereum account defining 24 | * DID identifier itself 25 | * Associated public key 26 | * Ed25519 signature owner identifier proving ownership 27 | * After migration, clients in order to resolve legacy identifier for DID document or a specific entity: 28 | * firstly should get a new identifier of the required Document DID or another entity, using DID or resource mapping 29 | * next resolve with the new identifier 30 | 31 | ### Migration from `did:indy` to `did:indy:besu` 32 | 33 | At some point company managing (Issuer,Holder,Verifier) decide to migrate from [Indy Node](https://github.com/hyperledger/indy-node) to [Indy Besu Ledger](https://github.com/hyperledger/indy-besu). 34 | 35 | In order to do that, their Issuer's applications need to publish their data to Indy Besu Ledger. 36 | Issuer need to run migration tool manually (on the machine containing Indy Wallet storing Credential Definitions) which migrate data. 37 | 38 | * Issuer: 39 | * All issuer applications need to run migration tool manually (on the machine containing Indy Wallet with Keys and Credential Definitions) in order to move data to Indy Besu Ledger properly. The migration process consist of multiple steps which will be described later. 40 | * After the data migration, issuer services should issue new credentials using Indy Besu ledger. 41 | * Holder: 42 | * Holder applications can keep stored credentials as is. There is no need to run migration for credentials which already stored in the wallet. 43 | * Holder applications should start using Indy Besu ledger to resolve DID Documents, Schemas, Credential Definitions or other on-ledger entities once Issuer completed migration. 44 | * Verifier: 45 | * Verifier applications should start using Indy Besu ledger to resolve DID Documents, Schemas, Credential Definitions or other on-ledger entities once Issuer completed migration. 46 | * Verifier applications should keep using old styled restriction in order to request credentials which were received before the migration. 47 | 48 | 1. Wallet and Client setup 49 | 1. All applications need to integrate Indy Besu vdr library 50 | 2. DID ownership moving to Indy Besu Ledger: 51 | 1. Issuer create Ed25519 key (with seed) in the Indy Besu wallet 52 | 2. Issuer create a new Secp256k1 keypair in Indy Besu wallet 53 | 3. Issuer publish Secp256k1 key to Indy ledger using ATTRIB transaction: `{ "besu": { "key": secp256k1_key } }` 54 | * Now Indy Besu Secp256k1 key is associated with the Issuer DID which is published on the Indy Ledger. 55 | * ATTRIB transaction is signed with Ed25519 key. No signature request for `secp256k1_key`. 56 | 3. Issuer builds DID Document which will include: 57 | * DID - fully qualified form should be used: `did:besu::` of DID which was published as NYM transaction to Indy Ledger 58 | * Two Verification Methods must be included: 59 | * `Ed25519VerificationKey2018` key published as NYM transaction to Indy Ledger 60 | * Key must be represented in multibase as base58 form was deprecated 61 | * `EcdsaSecp256k1VerificationKey2019` key published as ATTRIB transaction to Indy Ledger 62 | * Key must be represented in multibase 63 | * This key will be used in future to sign transactions sending to Indy Besu ledger 64 | * Transaction signature proves ownership of the key 65 | * Indy Besu account will be derived from the public key part 66 | * Two corresponding authentication methods must be included. 67 | * Service including endpoint which was published as ATTRIB transaction to Indy Ledger 68 | 4. Issuer publishes DID Document to Indy Besu ledger: 69 | ``` 70 | let did_doc = build_did_doc(&issuer.did, &issuer.edkey, &issuer.secpkey, &issuer.service); 71 | let receipt = DidRegistry::create_did(&client, &did_document).await 72 | ``` 73 | * Transaction is signed using Secp256k1 key `EcdsaSecp256k1VerificationKey2019`. 74 | * This key is also included into Did Document associated with DID. 75 | * Transaction level signature validated by the ledger that proves key ownership. 76 | * `Ed25519VerificationKey2018` - Indy Besu ledger will not require signature for proving ownership this key. 77 | * key just stored as part of DID Document and is not validated 78 | * potentially, we can add verification through the passing an additional signature 79 | ``` 80 | { 81 | context: "https://www.w3.org/ns/did/v1", 82 | id: "did:indy:besu:sovrin:staging:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 83 | controller: "did:indy:besu:sovrin:staging:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 84 | verificationMethod: [ 85 | { 86 | id: "did:indy:besu:sovrin:staging:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266#KEY-1", 87 | type: Ed25519VerificationKey2018, 88 | controller: "did:indy:besu:sovrin:staging:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 89 | publicKeyMultibase: "zH3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" 90 | }, 91 | { 92 | id: "did:indy:besu:sovrin:staging:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266#KEY-2", 93 | type: EcdsaSecp256k1VerificationKey2019, 94 | controller: "did:indy:besu:sovrin:staging:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 95 | publicKeyMultibase: "zNaqS2qSLZTJcuKLvFAoBSeRFXeivDfyoUqvSs8DQ4ajydz4KbUvT6vdJyz8i9gJEqGjFkCN27niZhoAbQLgk3imn 96 | } 97 | ], 98 | authentication: [ 99 | "did:indy:besu:sovrin:staging:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266#KEY-1", 100 | "did:indy:besu:sovrin:staging:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266#KEY-2" 101 | ], 102 | assertionMethod: [], 103 | capabilityInvocation: [], 104 | capabilityDelegation: [], 105 | keyAgreement: [], 106 | service: [ 107 | { 108 | id: "#inline-1", 109 | type: "DIDCommService", 110 | serviceEndpoint: "127.0.0.1:5555" 111 | } 112 | ] 113 | } 114 | ``` 115 | 5. Issuer builds and publishes all connected entities as Schemas and Credential Definitions on Indy Besu ledger 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /spec/5_target_systems.md: -------------------------------------------------------------------------------- 1 | ## Target System(s) 2 | 3 | The `did:indy` DID method applies to all DIDs which are anchored to a Hyperledger Indy Ledger and which comply with these specific conventions. 4 | -------------------------------------------------------------------------------- /spec/6_motivation_and_assumptions.md: -------------------------------------------------------------------------------- 1 | ## Motivation and Assumptions 2 | 3 | ### Assumption: Number of Indy Instances 4 | 5 | We are anticipating that there will be at most on the order of "low hundreds" of Indy network instances, with the potential for several (usually 3) subnamespaces per network instance for production, test (staging), and development deployments. This assumption is based on the likelihood of their being some (likely small) number of global Indy instances, and some number of national Indy instances. Assuming at most one per country, we would have around 200-300 total, leading to the anticipated maximum of "low hundreds" of Indy network instances. 6 | 7 | ### Including a Network-specific Identifier 8 | 9 | Including a network-specific identifier within an Indy DID identifier enables a "network of networks" capability where an Indy DID can be uniquely resolved to a specific DIDDoc stored on a specific Indy network using a common resolver software component. Given a `did:indy` DID, the network-specific identifier embedded in the DID can be extracted to determine where to send the request to resolve the DID Doc. This enables several useful properties: 10 | 11 | - Decentralization: Additional instances of networks can be deployed and easily used. 12 | - Scalability: Additional DID ledgers can be deployed and DIDs on those ledgers easily referenced. 13 | - Fit for purpose: A DID ledger can be deployed and easily used for specific purposes, such as part of a nation's critical infrastructure. 14 | - Governance: A DID ledger can be deployed under a specific governance structure appropriate to those writing to that ledger. 15 | 16 | ### Aligning Indy with the DID Core Specification 17 | 18 | The DID Indy method specification formalizes the transformation of an Indy ledger object (a [[ref: NYM]]) into a DIDDoc as defined in the DID Core specification from W3C, ensuring that identifiers written to and read from Indy ledgers are W3C standard DIDs. 19 | 20 | In case of using `did:indy:besu` DID Document, it fully conforms to the [Decentralized Identifiers (DIDs) Core specification](https://https://www.w3.org/TR/did-core/). 21 | 22 | ### Resolving a DIDDoc in a single transaction 23 | 24 | Previous approaches to resolving Indy ledger objects into DIDDocs required the client read one or more ledger objects (notably, [[ref: NYM]]s and [[ref: ATTRIB]]s) before assembly. This is at best "challenging" for the client, and at worst, extremely slow without specialized ledger support, particularly when a non-current version of the DID is being resolved. A preferred approach is to enable the resolution of a DID via a single read transaction that returns a single object off the ledger, including a state proof for that object. 25 | 26 | ### Cross-Ledger Object References 27 | 28 | The [[ref: DID Document]] controller of all objects on an Indy ledger MUST reside on the same Indy ledger as the object. Thus, the DID (e.g. the Indy [[ref: NYM]]) of the Issuer of a verifiable credential type must reside on the same ledger as the [[ref: CRED_DEF]], [[ref: REV_REG_DEF]] and [[ref: REV_REG_ENTRY]] objects for that type of verifiable credential. 29 | 30 | Note that the constraint above does not apply to a [[ref: SCHEMA]] referenced by a [[ref: CRED_DEF]], since a [[ref: CRED_DEF]] may use a [[ref: SCHEMA]] written by another [[ref: NYM]] or [[ref: DID Document]]. As such, a [[ref: CRED_DEF]] may reference a [[ref: SCHEMA]] on a different Indy ledger. 31 | -------------------------------------------------------------------------------- /spec/7_indy_did_method_identifiers.md: -------------------------------------------------------------------------------- 1 | ## Indy DID Method Identifiers 2 | 3 | The did:indy Method DID identifier has four components that are concatenated to make a DID specification conformant identifier. The components are: 4 | 5 | - **DID**: the hardcoded string `did:` to indicate the identifier is a DID. 6 | - **DID Indy Method**: the hardcoded string `indy:` indicating that the identifier uses this DID Method specification. The hardcoded string `indy:besu:` indicates the use of [`indy-besu`](https://github.com/hyperledger/indy-besu) implementation for Indy DID Method. 7 | - **DID Indy Namespace**: a string that identifies the name of the primary Indy ledger, followed by a `:`. The namespace string may optionally have a secondary ledger name prefixed by a `:` following the primary name. If there is no secondary ledger element, the DID resides on the primary ledger, else it resides on the secondary ledger. By convention, the primary is a production ledger while the secondary ledgers are non-production ledgers (e.g. staging, test, development) associated with the primary ledger. Examples include, `sovrin`, `sovrin:staging` and `idunion`. 8 | - **Namespace Identifier**: an identifier unique to the given DID Indy namespace. 9 | - The identifier may be self-certifying, meaning that the identifier is derived from the initial verkey associated with the identifier. See the [DID Creation](#nym-transaction-version) section of this document for the derivation details. 10 | - The identifier of `did:indy:besu` is an Ethereum address similarly to `did:ethr` method, but there multiple significant differences between them: 11 | - API consist of more traditional `create`, `update`, `deactivate` methods 12 | - The associated `Did Document` is stored in the contract storage in complete form 13 | - In order to resolve Did Document you only need to call single method 14 | - DID must be registered by executing one of `create` contract methods 15 | - State proof can be obtained for resolved Did Record 16 | 17 | The components are assembled as follows: 18 | 19 | `did:indy::` 20 | 21 | `did:indy:besu::` 22 | 23 | Some examples of `did:indy` DID Method identifiers are: 24 | 25 | * A DID written to the Sovrin MainNet ledger: 26 | * `did:indy:sovrin:7Tqg6BwSSWapxgUDm9KKgg` 27 | * A DID written to the Sovrin StagingNet ledger: 28 | * `did:indy:sovrin:staging:6cgbu8ZPoWTnR5Rv5JcSMB` 29 | * A DID on the IDUnion Test ledger: 30 | * `did:indy:idunion:test:2MZYuPv2Km7Q1eD4GCsSb6` 31 | * A DID written to the Sovrin StagingNet ledger used Indy Besu implementation: 32 | * `did:indy:besu:sovrin:staging:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266` 33 | -------------------------------------------------------------------------------- /spec/8_other_indy_ledger_object_identifiers.md: -------------------------------------------------------------------------------- 1 | ## Other Indy Ledger Object Identifiers 2 | 3 | Indy ledgers may hold object types other than DIDs, and each of the other object types must also be resolvable to a specific Indy network instance. The identifiers for these objects are used in data structures that are exchanged by Indy clients (e.g. Aries Agents)--verifiable credentials, presentation requests, presentations and so on. Transitioning to the `did:indy` DID Method requires transitioning Indy clients/resolvers to use the identifiers defined in this section. 4 | 5 | ### DID URLs for Indy Object Identifiers 6 | 7 | The structure of identifiers for all non-DID Indy ledger objects is the following DID URL structure, based on the DID of the object's DID controller: 8 | 9 | - `////` 10 | 11 | The components of the DID URL are: 12 | 13 | - `` the `did:indy` DID of the object-owning controller 14 | - `` family of the object 15 | - `` version of the object family 16 | - `` one of [[ref: SCHEMA]], [[ref: CRED_DEF]], [[ref: REV_REG_DEF]], [[ref: REV_REG_ENTRY]], [[ref: ATTRIB]] 17 | - `` an object type unique identifier defined by Indy by object type. 18 | 19 | The data returned from resolving such DID URLs is the ledger object and relevant state proof; the same data returned from the Indy Node read object transactions, such as the [GET_SCHEMA](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-schema) transaction, and dependent on the type of the object. 20 | 21 | Since indy allows special characters within the names of the different ledger objects, percent encoding according to [Section 2 of RFC3986](https://datatracker.ietf.org/doc/html/rfc3986#section-2) has to be applied to access these objects via DID URLs. 22 | 23 | While there are no restrictions regarding the used characters, we strongly encourage avoiding special characters in the names of ledger objects. 24 | 25 | The following sections cover each ledger object type, providing: 26 | 27 | - an example DID URL identifier, 28 | - a link to an example object residing on the Sovrin MainNet Indy ledger (courtesy of [indyscan.io](https://indyscan.io)), 29 | - the appropriate object family and version, 30 | - the format of the response when resolving the DID URL, 31 | - the pre-`did:indy` identifier for each object, and 32 | - notes about the elements of the pre-`did:indy` identifier. 33 | 34 | This first version of the `did:indy` DID Method will use an `` value of `anoncreds` and an `` of `v0` to match the 35 | pre-specification, open source version of anoncreds as implemented in the [indy-sdk](https://github.com/hyperledger/indy-sdk/tree/master/docs/design/002-anoncreds). 36 | Later versions of the `did:indy` specification will use a higher `` as the AnonCreds standardization work proceeds 37 | and the required dependency on Hyperledger Indy is removed. In this initial version, the DID URLs are closely aligned with the existing object identifiers. 38 | 39 | #### Schema 40 | 41 | DID URL: [`did:indy:sovrin:F72i3Y3Q4i466efjYJYCHM/anoncreds/v0/SCHEMA/npdb/4.3.4`](https://indyscan.io/tx/SOVRIN_MAINNET/domain/56495) 42 | 43 | - Object Family: `anoncreds` 44 | - Family Version: `v0` 45 | - Object Type: `SCHEMA` 46 | - Name, example `npdb`: The client-defined schema name 47 | - Schema Version, example `4.3.4`: The client-defined version of the [[ref: SCHEMA]] 48 | 49 | Response: Same as the Indy Node [GET_SCHEMA Txn](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-schema) 50 | 51 | Existing identifier: `F72i3Y3Q4i466efjYJYCHM:2:npdb:4.3.4` 52 | 53 | - `2` is the enumerated object type 54 | - Name and Schema Version elements defined above. 55 | 56 | #### Cred Def 57 | 58 | DID URL: [`did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/CLAIM_DEF/56495/npdb`](https://indyscan.io/tx/SOVRIN_MAINNET/domain/56496) 59 | 60 | - Object Family: `anoncreds` 61 | - Family Version: `v0` 62 | - Schema ID 63 | - Example `56495`: A unique identifier for the schema upon which the CredDef is defined. In v0, the value is also the Hyperledger Indy instance sequence number for the Schema object used by this Cred Def. 64 | - Example `did:indy:besu:testnet:0xf0e2db6c8dc6c681bb5d6ad121a107f300e9b2b5/anoncreds/v0/SCHEMA/F1DClaFEzi3t/1.0.0`: A Schema DID URL lake a unique identifier 65 | - In later versions, we expect that the schema identifier will either be removed from the CredDef DID URL, or take a different form. 66 | - Name, example `npdb`: The client-defined cred def name/tag. 67 | 68 | Response: Same as the Indy Node [GET_CLAIM_DEF Txn](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-claim-def) 69 | 70 | Existing identifier: `5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb` 71 | 72 | - `3` is the enumerated object type 73 | - `CL` is the signature type for the cred def, which is `CL` for all cred defs on all existing Indy ledgers 74 | - Schema ID and Name elements defined above. 75 | 76 | We recommend that AnonCred credential issuers use a unique Name item per Cred Def, and not rely on the embedded Schema ID 77 | remaining in the DID URL for a Cred Def in future versions of the `did:indy` method. 78 | 79 | #### Revocation Registry Definition 80 | 81 | DID URL: [`did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_DEF/56495/npdb/TAG1`](https://indyscan.io/tx/SOVRIN_MAINNET/domain/56497) 82 | 83 | - Object Family: `anoncreds` 84 | - Family Version: `v0` 85 | - Schema ID, example `56495`: A unique identifier for the schema upon which the CredDef/RevReg are defined. In v0, the value is also the Hyperledger Indy instance sequence number for the Schema object used by this Cred Def. 86 | In later versions, we expect that the schema identifier will either be removed from the RevReg DID URL, or take a different form. 87 | - Cred Def Name, example `npdb`: The client-defined cred def name. 88 | - Tag, example `TAG1`: The client-defined rev reg tag (name). 89 | 90 | Response: Same as the Indy Node [GET_REVOC_REG_DEF Txn](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-revoc-reg-def) 91 | 92 | Existing Identifier: `5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1` 93 | 94 | - `4` is the enumerated object type 95 | - `5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb` is the identifier of the associated Cred Def 96 | - Tag element defined above. 97 | 98 | #### Revocation Registry Entry 99 | 100 | DID URL: [`did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_ENTRY/56495/npdb/TAG1`](https://indyscan.io/tx/SOVRIN_MAINNET/domain/58567) 101 | 102 | - Object Family: `anoncreds` 103 | - Family Version: `v0` 104 | - Schema ID, example `56495`: A unique identifier for the schema upon which the CredDef/RevReg are defined. In v0, the value is also the Hyperledger Indy instance sequence number for the Schema object used by this Cred Def. 105 | In later versions, we expect that the schema identifier will either be removed from the RevReg DID URL, or take a different form. 106 | - Cred Def Name, example `npdb`: The client-defined cred def name. 107 | - Tag, example `TAG1`: The client-defined rev reg tag (name). 108 | 109 | The DID URL resolution response depends on the query parameters used, as follows: 110 | 111 | - None 112 | - Response is the same as the Indy Node [GET_REVOC_REG Txn](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-revoc-reg) with the current time used for the `versionTime`. 113 | - `?versionTime=` 114 | - Response is the same as the Indy Node [GET_REVOC_REG Txn](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-revoc-reg). 115 | - `?from=&to=` 116 | - Response is the same as the Indy Node [GET_REVOC_REG_DELTA Txn](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-revoc-reg-delta) 117 | - At least one of the parameters has to be set. 118 | - If only `from` is set, then `to` is implicitly set to the current time. 119 | - If only `to` is set, then all deltas up to this time are returned. 120 | 121 | Existing Identifier: `5:5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1` 122 | 123 | - `5` is the enumerated object type 124 | - The remainder of the identifier is the identifier for the applicable Revocation Registry 125 | 126 | #### ATTRIB 127 | 128 | No DID URL representation is defined for the Hyperledger Indy [[ref: ATTRIB]] object, as 129 | the use of the ATTRIB object is **deprecated** with the introduction of the 130 | `did:indy` DID Method. Where an ATTRIB might have been used in the past, an Indy 131 | client updated for `did:indy` should put the required data directly into the 132 | `diddocContent` item in a DID (NYM) update transaction. 133 | -------------------------------------------------------------------------------- /spec/9_finding_indy_ledgers.md: -------------------------------------------------------------------------------- 1 | ## Finding Indy Ledgers 2 | 3 | To connect and read or write from a Hyperledger Indy network instance, a client must have the configuration file (in Indy, called the "gensis" file) for the network. Given a `did:indy` DID (e.g. `did:indy::`), the Indy network instance on which the DID resides is known. However, there remains a challenge for the entity interested in resolving the DID—finding the genesis file for that network instance. The following documents two mechanisms resolvers can use to access required genesis files. 4 | 5 | ### Static 6 | 7 | A client that will resolve Hyperledger Indy DIDs can be statically configured to "know" about a set of Indy networks by loading the files on startup. The files would be collected from the node operators in some way by those deploying the client software (e.g. an Aries Wallet). 8 | 9 | When a static list of Indy networks is used, DIDs from other, organically discovered networks not on the list cannot be resolved by the client. 10 | 11 | ### Dynamic using GitHub 12 | 13 | The Hyperledger Indy GitHub repo `indy-did-networks` enables Indy DID network operators to publish their network genesis files in a standard way. Within the repo, the folder "networks" contains a folder per primary network. Within each network folder is the genesis file for the primary network, and folders (containing a corresponding genesis file) for each subspace network. The naming format for the genesis files is: 14 | 15 | - `pool_transactions_genesis.json` 16 | 17 | For example, the Sovrin MainNet, StagingNet and BuilderNet genesis files will be in the repo as: 18 | 19 | - `networks/sovrin/pool_transactions_genesis.json` 20 | - `networks/sovrin/staging/pool_transactions_genesis.json` 21 | - `networks/sovrin/builder/pool_transactions_genesis.json` 22 | 23 | The committers to the repo for each network SHOULD include in the folder at least a README.md file with information about the network, plus any additional documents about the ledger instance, such as Governance Framework documents. 24 | 25 | #### Client Usage 26 | 27 | A client may retrieve selected network genesis files from the repo to use as their set of static files, as described in the [previous section](#static). The developers of the clients can monitor the repo for changes to the genesis files that they are using. 28 | 29 | If a DID is obtained by the client that is from a network not already known by the client, the client MAY look for the unknown (to the client) network in the GitHub repo and decide to use (or not) the associated genesis file to connect to the network. 30 | 31 | The security policy of the client (and perhaps the user of the client) might give options about handling unknown networks, such as: 32 | 33 | - Never connect. 34 | - Review and connect if permission from client operator granted. 35 | - Always connect if the genesis file for the network can be found. 36 | 37 | #### Repository Maintenance 38 | 39 | Each contributing network instance operator maintains copies of their genesis files and supporting documents in the GitHub repo by submitting Pull Requests (PRs) to the repo. The community selected repo maintainers are expected to merge PRs with limited review based on their knowledge of the network operators. Their focus is not to provide editorial oversight but only to: 40 | 41 | - prevent updates to a network's genesis file by other than known operator of the network, and 42 | - prevent badly formatted genesis files from being added to the repository. 43 | 44 | The maintainers are authorized to submit PRs to remove "bad actor" network folders based on notifications from the community and followup verification. 45 | 46 | #### GitHub Update Disputes 47 | 48 | Any disputes about the handling of PRs submitted to the repo should be escalated through the Indy Community (via the #indy channel on Hyperledger chat and/or at the [Indy Contributors](https://wiki.hyperledger.org/display/indy/Indy+Contributors+Meeting) call or its successor). If the issue is not resolved at the Indy level, the issue should be escalated to Hyperledger leadership (the Executive Director or the Technical Steering Committee). 49 | -------------------------------------------------------------------------------- /specs.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "specs": [ 4 | { 5 | "title": "Indy DID Method Specification", 6 | "spec_directory": "./spec", 7 | "output_path": "./docs", 8 | "markdown_paths": [ 9 | "0_header.md", 10 | "1_about.md", 11 | "2_abstract.md", 12 | "3_indy_ledger_object_glossary.md", 13 | "4_differences_from_did_sov.md", 14 | "5_target_systems.md", 15 | "6_motivation_and_assumptions.md", 16 | "7_indy_did_method_identifiers.md", 17 | "8_other_indy_ledger_object_identifiers.md", 18 | "9_finding_indy_ledgers.md", 19 | "10_original_did_operations.md", 20 | "11_besu_did_operations.md", 21 | "12_did_indy_did_component_syntax.md", 22 | "13_json_or_json-ld.md", 23 | "14_privacy_considerations.md", 24 | "15_security_considerations.md", 25 | "16_future_directions.md" 26 | ], 27 | "logo": "https://raw.githubusercontent.com/hyperledger/indy-did-method/main/hyperledger-logo.svg", 28 | "logo_link": "https://github.com/hyperledger/indy-did-method", 29 | "source": { 30 | "host": "github", 31 | "account": "hyperledger", 32 | "repo": "indy-did-method" 33 | } 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /src/asset-map.json: -------------------------------------------------------------------------------- 1 | { 2 | "head": { 3 | "css": [ 4 | "assets/css/custom-elements.css", 5 | "assets/css/prism.css", 6 | "assets/css/chart.css", 7 | "assets/css/index.css" 8 | ], 9 | "js": [ 10 | "assets/js/utils.js", 11 | "assets/js/custom-elements.js" 12 | ] 13 | }, 14 | "body": { 15 | "js": [ 16 | "assets/js/markdown-it.js", 17 | "assets/js/prism.js", 18 | "assets/js/mermaid.js", 19 | "assets/js/chart.js", 20 | "assets/js/font-awesome.js", 21 | "assets/js/popper.js", 22 | "assets/js/tippy.js", 23 | "assets/js/index.js" 24 | ] 25 | } 26 | } -------------------------------------------------------------------------------- /src/markdown-it-extensions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const levels = 2; 4 | const openString = '['.repeat(levels); 5 | const closeString = ']'.repeat(levels); 6 | const contentRegex = /\s*([^\s\[\]:]+):?\s*([^\]\n]+)?/i; 7 | 8 | module.exports = function(md, templates = {}) { 9 | 10 | md.inline.ruler.after('emphasis', 'templates', function templates_ruler(state, silent) { 11 | 12 | var start = state.pos; 13 | let prefix = state.src.slice(start, start + levels); 14 | if (prefix !== openString) return false; 15 | var indexOfClosingBrace = state.src.indexOf(closeString, start); 16 | 17 | if (indexOfClosingBrace > 0) { 18 | 19 | let match = contentRegex.exec(state.src.slice(start + levels, indexOfClosingBrace)); 20 | if (!match) return false; 21 | 22 | let type = match[1]; 23 | let template = templates.find(t => t.filter(type) && t); 24 | if (!template) return false; 25 | 26 | let args = match[2] ? match[2].trim().split(/\s*,+\s*/) : []; 27 | let token = state.push('template', '', 0); 28 | token.content = match[0]; 29 | token.info = { type, template, args }; 30 | if (template.parse) { 31 | token.content = template.parse(token, type, ...args) || token.content; 32 | } 33 | 34 | state.pos = indexOfClosingBrace + levels; 35 | return true; 36 | } 37 | 38 | return false; 39 | }); 40 | 41 | md.renderer.rules.template = function(tokens, idx, options, env, renderer) { 42 | let token = tokens[idx]; 43 | let template = token.info.template; 44 | if (template.render) { 45 | return template.render(token, token.info.type, ...token.info.args) || (openString + token.content + closeString); 46 | } 47 | return token.content; 48 | } 49 | 50 | let pathSegmentRegex = /(?:http[s]*:\/\/([^\/]*)|(?:\/([^\/?]*)))/g; 51 | md.renderer.rules.link_open = function(tokens, idx, options, env, renderer) { 52 | let token = tokens[idx]; 53 | let attrs = token.attrs.reduce((str, attr) => { 54 | let name = attr[0]; 55 | let value = attr[1]; 56 | if (name === 'href') { 57 | let index = 0; 58 | value.replace(pathSegmentRegex, (m, domain, seg) => { 59 | str += `path-${index++}="${domain || seg}"`; 60 | }); 61 | } 62 | return str += name + '="' + value + '" '; 63 | }, ''); 64 | let anchor = ``; 65 | return token.markup === 'linkify' ? anchor + '' : anchor; 66 | } 67 | 68 | md.renderer.rules.link_close = function(tokens, idx, options, env, renderer) { 69 | return tokens[idx].markup === 'linkify' ? '' : ''; 70 | } 71 | 72 | }; --------------------------------------------------------------------------------