<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Darshit Khandelwal</title>
    <description>The latest articles on DEV Community by Darshit Khandelwal (@darshit2308).</description>
    <link>https://dev.to/darshit2308</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3984308%2Fe0cd52d3-4766-4ca8-976b-f690d710cef8.png</url>
      <title>DEV Community: Darshit Khandelwal</title>
      <link>https://dev.to/darshit2308</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/darshit2308"/>
    <language>en</language>
    <item>
      <title>Week -02</title>
      <dc:creator>Darshit Khandelwal</dc:creator>
      <pubDate>Tue, 30 Jun 2026 14:21:36 +0000</pubDate>
      <link>https://dev.to/darshit2308/week-02-1bol</link>
      <guid>https://dev.to/darshit2308/week-02-1bol</guid>
      <description>&lt;h1&gt;
  
  
  Building the GitHubContributorCredential: SD-JWT Design and Hedera DID Verification
&lt;/h1&gt;

&lt;p&gt;This week's goals were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define the complete &lt;code&gt;GitHubContributorCredential&lt;/code&gt; schema, including contributor DID, GitHub username, GitHub numeric account ID, verified GPG fingerprint, issuer DID, issuance time, expiry, and credential status reference.&lt;/li&gt;
&lt;li&gt;Design the SD-JWT disclosure policy and determine which claims are always disclosed versus selectively disclosed.&lt;/li&gt;
&lt;li&gt;Create example credential payloads and schema fixtures that can be reused throughout implementation and testing.&lt;/li&gt;
&lt;li&gt;Validate the credential model with unit tests covering valid and invalid credential subjects.&lt;/li&gt;
&lt;li&gt;Create and resolve a &lt;code&gt;did:hedera&lt;/code&gt; DID on Hedera testnet using the operator credentials agreed during Week 1.&lt;/li&gt;
&lt;li&gt;Capture proof of successful DID creation and resolution through HashScan and resolver outputs.&lt;/li&gt;
&lt;li&gt;Implement deterministic verification-method selection so signing operations never depend on array ordering such as &lt;code&gt;verificationMethod[0]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add tests covering DID resolution and verification-method selection behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's how the week went, day by day.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Defining the GitHubContributorCredential schema
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;GitHubContributorCredential&lt;/code&gt; holds every field our system needs to identify a contributor. To keep it usable by the Heka backend service, we split the fields into two categories: &lt;strong&gt;standard VC metadata&lt;/strong&gt; (the outer wrapper) and the &lt;strong&gt;credential subject&lt;/strong&gt; (the actual identity claims).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Standard VC metadata&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Issuer DID&lt;/strong&gt; — the &lt;code&gt;did:hedera&lt;/code&gt; identifier of the Heka platform issuing the credential.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issuance time&lt;/strong&gt; — the ISO 8601 timestamp of when the proof was generated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expiry&lt;/strong&gt; — when the credential needs to be renewed (e.g., six months or a year from issuance).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credential status reference&lt;/strong&gt; — a pointer to the revocation registry on the Hedera network, so Heka can revoke the credential if a contributor's keys are compromised.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Credential subject (the contributor claims)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Contributor DID&lt;/strong&gt; — the developer's personal &lt;code&gt;did:hedera&lt;/code&gt; identifier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub username&lt;/strong&gt; — the human-readable handle (e.g., &lt;code&gt;darshit2308&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub numeric account ID&lt;/strong&gt; — the immutable integer ID GitHub assigns to every account. This matters because a user can change their handle, but the numeric ID never changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verified GPG fingerprint&lt;/strong&gt; — the cryptographic fingerprint of the key used to sign commits, proving the holder controls the private key registered against their GitHub account.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Putting it all together, here's the schema draft:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://www.w3.org/2018/credentials/v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://w3id.org/security/suites/ed25519-2020/v1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"VerifiableCredential"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GitHubContributorCredential"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issuer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"did:hedera:testnet:z6MkhaXgBZDvotDkL5257faiztiuC2ZXOS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issuanceDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-28T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expirationDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2027-06-28T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"credentialStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://heka.network/status/123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CredentialStatusList2020"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"credentialSubject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"did:hedera:testnet:z6Mkq..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"githubUsername"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"darshit2308"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"githubAccountId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4115704&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"gpgFingerprint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3AA5C34371567BD2"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick walkthrough for anyone new to verifiable credentials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@context&lt;/code&gt; tells any parser "read this using W3C Verifiable Credentials rules" — it gives the document its vocabulary.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt; is just a unique identifier for the credential document itself.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;type&lt;/code&gt; declares what kind of credential this is.&lt;/li&gt;
&lt;li&gt;Everything else maps to the eight fields described above.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note for later:&lt;/strong&gt; &lt;code&gt;CredentialStatusList2020&lt;/code&gt; isn't an officially standardized status type — the current W3C recommendation is the &lt;a href="https://www.w3.org/TR/vc-bitstring-status-list/" rel="noopener noreferrer"&gt;Bitstring Status List&lt;/a&gt; spec (&lt;code&gt;BitstringStatusListEntry&lt;/code&gt;). Worth revisiting before this goes to production, even if &lt;code&gt;CredentialStatusList2020&lt;/code&gt; stays as an internal placeholder for now.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  2. Designing the SD-JWT disclosure policy
&lt;/h2&gt;

&lt;p&gt;Quick primer on SD-JWT (Selective Disclosure JWT) for anyone unfamiliar: it lets a credential holder reveal only the claims required to verify something, without exposing everything else. A simple analogy — a bouncer checking you're over 18 doesn't need your phone number or home address, just proof of age.&lt;/p&gt;

&lt;p&gt;So we split &lt;code&gt;GitHubContributorCredential&lt;/code&gt; into two buckets: fields that are &lt;strong&gt;always disclosed&lt;/strong&gt; (mandatory for the system to function) and fields that are &lt;strong&gt;selectively disclosed&lt;/strong&gt; (hidden by default, to protect the developer's privacy).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always disclosed&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Issuer DID&lt;/strong&gt; — Heka needs to know who issued the credential.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issuance &amp;amp; expiry dates&lt;/strong&gt; — Heka needs to verify the time limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credential status&lt;/strong&gt; — Heka must be able to check whether it's been revoked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contributor DID (&lt;code&gt;sub&lt;/code&gt;)&lt;/strong&gt; — the developer's public identifier on the Hedera network.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Selectively disclosed&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub username&lt;/strong&gt; — a developer may want to prove they're an authorized LFDT contributor without permanently linking their Hedera DID to a public GitHub handle anyone could scrape.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub numeric account ID&lt;/strong&gt; — kept hidden unless the backend specifically needs to map the DID to an exact GitHub account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verified GPG fingerprint&lt;/strong&gt; — only revealed when a repository explicitly needs it to verify a signed commit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Example payloads and schema fixtures
&lt;/h2&gt;

&lt;p&gt;Since we won't reveal every field by default, the SD-JWT payload replaces the hidden fields with an &lt;code&gt;_sd&lt;/code&gt; (selective disclosure) array of hashes. A first pass looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"did:hedera:testnet:z6MkhaXgBZDvotDkL5257faiztiuC2ZXOS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1782604800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1814140800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"did:hedera:testnet:z6Mkq..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GitHubContributorCredential"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"_sd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"JzYx0... (hash of githubUsername)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Qp2Lm... (hash of githubAccountId)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Xy9Rq... (hash of gpgFingerprint)"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"_sd_alg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha-256"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Public (always-disclosed) fields: &lt;code&gt;iss&lt;/code&gt; (issuer), &lt;code&gt;iat&lt;/code&gt; (issuance time), &lt;code&gt;exp&lt;/code&gt; (expiry), &lt;code&gt;sub&lt;/code&gt; (contributor DID), and &lt;code&gt;vct&lt;/code&gt; (the credential type — since it's not in &lt;code&gt;_sd&lt;/code&gt;, it's visible by default too).&lt;/p&gt;

&lt;p&gt;Hidden fields: username, account ID, GPG fingerprint.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;_sd_alg&lt;/code&gt; declares exactly which hash function was used, so the verifier knows how to check the disclosures later.&lt;/p&gt;

&lt;p&gt;The verifier can't just brute-force the hidden fields from the hashes, since that would defeat the point — anyone could disclose everything by guessing. So each hidden claim is paired with a random salt before hashing: the disclosure is really &lt;code&gt;hash(salt + claim_name + claim_value)&lt;/code&gt;, and only the holder can produce the matching salt+value pair when asked to disclose it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where this landed
&lt;/h3&gt;

&lt;p&gt;The finished schema fixture (JSON Schema for the credential subject):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://json-schema.org/draft/2020-12/schema"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GitHubContributorCredentialSubject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The exact rules for Heka GitHub Contributor claims"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^did:hedera:.*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The developer's Hedera DID"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"githubUsername"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"minLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"githubAccountId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"gpgFingerprint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^[0-9A-F]{16,40}$"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Uppercase HEX fingerprint"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"githubUsername"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"githubAccountId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gpgFingerprint"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"additionalProperties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; the &lt;code&gt;gpgFingerprint&lt;/code&gt; pattern allows 16–40 hex characters. A full GPG fingerprint is conventionally 40 hex characters (SHA-1, 160-bit); a 16-character value is really a GPG &lt;em&gt;key ID&lt;/em&gt;, not a fingerprint. Worth tightening this to exactly 40 characters unless we genuinely want to accept short key IDs too.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And the final credential payload fixture, now including &lt;code&gt;credentialStatus&lt;/code&gt; so it matches the "always disclosed" policy from Section 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"did:hedera:testnet:z6MkhaXgBZDvotDkL5257faiztiuC2ZXOS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1782604800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1814140800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"did:hedera:testnet:z6MkqExampleDeveloperDID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GitHubContributorCredential"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"credentialStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://heka.network/status/123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CredentialStatusList2020"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"_sd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"JzYx0aB9... (hash of 'githubUsername': 'darshit2308')"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Qp2LmC4... (hash of 'githubAccountId': 4115704)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Xy9RqZ1... (hash of 'gpgFingerprint': '3AA5C34371567BD2')"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"_sd_alg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha-256"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These live in the repo as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;heka-identity-service/src/credentials/fixtures/github-contributor-credential.schema.json&lt;/code&gt; — the JSON Schema defining what a valid &lt;code&gt;GitHubContributorCredential&lt;/code&gt; subject looks like, the same way a government ID schema defines which fields (name, date of birth, etc.) must be present.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heka-identity-service/src/credentials/fixtures/mock-sd-jwt-payload.json&lt;/code&gt; — a mock &lt;code&gt;GitHubContributorCredential&lt;/code&gt; for a fictional contributor, used across tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Validating the model with Jest
&lt;/h2&gt;

&lt;p&gt;This was my first time writing Jest tests, so a quick summary of what I learned.&lt;/p&gt;

&lt;p&gt;We use &lt;strong&gt;AJV&lt;/strong&gt; (Another JSON &lt;em&gt;Schema&lt;/em&gt; Validator) to check whether a piece of JSON data conforms to a given JSON Schema — for example, confirming &lt;code&gt;githubAccountId&lt;/code&gt; is actually an integer, as the schema requires.&lt;/p&gt;

&lt;p&gt;The tests live in &lt;code&gt;heka-identity-service/src/credentials/credential-subject.spec.ts&lt;/code&gt;. The &lt;code&gt;.spec.ts&lt;/code&gt; suffix is Jest's convention for a specification (test) file, and its job is to verify the schema behaves correctly against both valid and invalid inputs.&lt;/p&gt;

&lt;p&gt;A small helper does the heavy lifting of turning a fixture file into a usable object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadFixture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fixtures&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, the test suite exercises as many valid and invalid permutations of the credential subject as we could think of — missing fields, wrong types, malformed DIDs, malformed fingerprints — to make sure AJV rejects exactly what it should and accepts exactly what it should.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. did:hedera DID creation and deterministic verification-method selection
&lt;/h2&gt;

&lt;p&gt;Using the operator credentials agreed on in Week 1, we created and resolved a &lt;code&gt;did:hedera&lt;/code&gt; DID on Hedera testnet, with proof of creation and resolution captured via HashScan and the resolver output.&lt;/p&gt;

&lt;p&gt;We also implemented deterministic verification-method selection, so signing operations always pick a specific, well-defined verification method rather than relying on array position (e.g., &lt;code&gt;verificationMethod[0]&lt;/code&gt;), which isn't guaranteed to be stable. Tests now cover both DID resolution and this selection logic.&lt;/p&gt;

&lt;p&gt;The code-level detail for both of these — along with the full schema, fixtures, and Jest suite above — is in the PR linked below; it's a better reference than a paraphrase here.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;PR for reference:&lt;/strong&gt; &lt;a href="https://github.com/hiero-ledger/heka-identity-platform/pull/179" rel="noopener noreferrer"&gt;hiero-ledger/heka-identity-platform#179&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>devjournal</category>
      <category>github</category>
      <category>web3</category>
    </item>
    <item>
      <title>Week - 1 (Phase 1)</title>
      <dc:creator>Darshit Khandelwal</dc:creator>
      <pubDate>Mon, 22 Jun 2026 14:10:41 +0000</pubDate>
      <link>https://dev.to/darshit2308/week-1-phase-1-fa6</link>
      <guid>https://dev.to/darshit2308/week-1-phase-1-fa6</guid>
      <description>&lt;h1&gt;
  
  
  Week 1 of My LFDT Mentorship: Architecture, Hedera, and Getting Everything Running
&lt;/h1&gt;

&lt;p&gt;Welcome to the first post of my LFDT (Linux Foundation Decentralized Trust) mentorship blog! Each week I'll be writing about what I built, what I learned, and — honestly — what confused me along the way.&lt;/p&gt;

&lt;p&gt;Week 1 was all about &lt;strong&gt;planning, architecture decisions, and setting up the groundwork&lt;/strong&gt; before any real code gets written. No flashy features yet, but a lot of important decisions that will shape everything that comes after. Let's get into it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Week at a Glance
&lt;/h2&gt;

&lt;p&gt;Here's a quick summary of everything on my plate this week:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Align the implementation plan with my mentor and confirm the interpretation of Issue #87&lt;/li&gt;
&lt;li&gt;Finalize the target architecture for the whole project&lt;/li&gt;
&lt;li&gt;Sort out the Hedera testnet setup and operator credentials&lt;/li&gt;
&lt;li&gt;Understand the revocation strategy and its scope during mentorship&lt;/li&gt;
&lt;li&gt;Set up the local Heka development environment&lt;/li&gt;
&lt;li&gt;Install and configure a GitHub App for local testing&lt;/li&gt;
&lt;li&gt;Create a webhook fixture repository for replaying PR events&lt;/li&gt;
&lt;li&gt;Prepare a PR slicing plan for incremental delivery&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Aligning with the Mentor &amp;amp; Locking Down the Plan
&lt;/h2&gt;

&lt;p&gt;The week started with a sync with my mentor to get the implementation plan nailed down. The good news is, the week-by-week plan has been finalized after a proper discussion, and the interpretation of &lt;strong&gt;Issue #87&lt;/strong&gt; has been confirmed. With the plan locked in, I can move forward with confidence in the coming weeks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deciding the Target Architecture
&lt;/h2&gt;

&lt;p&gt;One of the bigger decisions this week was figuring out &lt;em&gt;where&lt;/em&gt; everything actually lives, contributor onboarding, GitHub integration, credential issuance, DID management, and verification policy logic all need a home.&lt;/p&gt;

&lt;p&gt;Here's what we landed on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;heka-identity-service&lt;/code&gt;&lt;/strong&gt; — This is where the core modules live: contributor onboarding, credential issuance, DID management, and verification policy logic. All of this gets built as a dedicated module within this repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A separate GitHub repository&lt;/strong&gt; — The GitHub integration code (the Probot app, webhook handling, check-run posting) lives in its own repo, completely separate from &lt;code&gt;heka-identity-service&lt;/code&gt;. This keeps Heka clean and focused on identity, and makes the GitHub-specific code easier to extend or extract later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are still a few grey areas to iron out over the next couple of weeks, but the overall direction is clear and agreed upon.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Hedera: Consensus Nodes vs Mirror Nodes
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you're already familiar with Hedera's architecture, feel free to skip ahead to the next section!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before I explain how Hedera fits into this project, let me share something I was confused about early on: &lt;strong&gt;what's the difference between a consensus node and a mirror node?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unlike older blockchains (like Ethereum) that use a single node type for everything, Hedera splits the workload into two completely different node types to keep the network fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Consensus Node — The Write Database
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This is where you send new transactions (like creating an account or logging a DID document to the ledger).&lt;/li&gt;
&lt;li&gt;Its job is to receive the transaction, verify it, agree on a timestamp, and write it to the ledger as fast as possible.&lt;/li&gt;
&lt;li&gt;Because it's hyper-focused on speed and new writes, it's not great at answering questions about old or historical data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Mirror Node — The Read Database
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The mirror node sits quietly alongside the consensus node and constantly copies (mirrors) every approved transaction.&lt;/li&gt;
&lt;li&gt;Its only job is to take that raw blockchain data and organize it into a highly searchable traditional database (like PostgreSQL), and expose a REST API that developers can query.&lt;/li&gt;
&lt;li&gt;Important caveat: you &lt;strong&gt;cannot send new transactions&lt;/strong&gt; to the mirror node — it's strictly read-only.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How This Applies to Our Project
&lt;/h3&gt;

&lt;p&gt;In practice, the two node types serve two completely different purposes in our system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Writing (creating/updating a DID):&lt;/strong&gt; Our backend uses the &lt;strong&gt;consensus node&lt;/strong&gt;, authorized via operator credentials from our &lt;code&gt;.env&lt;/code&gt;. The SDK signs the transaction automatically using the operator key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reading (showing transaction history or verifying an identity):&lt;/strong&gt; The frontend makes a standard HTTP GET request directly to the &lt;strong&gt;mirror node's REST API&lt;/strong&gt; — completely bypassing the consensus node.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our &lt;code&gt;.env&lt;/code&gt; will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HEDERA_NETWORK=
OPERATOR_ID=
OPERATOR_KEY=
NODE_IP=
MIRROR_NODE_IP=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  HBAR Balance &amp;amp; Revocation Strategy
&lt;/h2&gt;

&lt;p&gt;After clarifying with my mentor Alexander, I now have the credentials for the operator account. The account comes loaded with enough test HBAR tokens to cover everything I need during development — creating, updating, and querying DIDs. So the token balance won't be a blocker at all.&lt;/p&gt;

&lt;p&gt;On the &lt;strong&gt;revocation front&lt;/strong&gt;: implementing a full, hardened VC revocation mechanism is out of scope for this mentorship. The focus right now is on building a working prototype, so revocation will either be tackled in the second half of the mentorship or saved for post-mentorship work. For now, the expectation is lightweight documentation and runbook-level treatment — not a working mechanism.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting Up the Local Dev Environment
&lt;/h2&gt;

&lt;p&gt;This was the most hands-on part of the week. Getting the local Heka environment running required wiring together three tools: &lt;strong&gt;Nginx&lt;/strong&gt;, &lt;strong&gt;Ngrok&lt;/strong&gt;, and a &lt;strong&gt;GitHub App&lt;/strong&gt;. Let me walk through how they fit together.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Nginx Routing Rules
&lt;/h3&gt;

&lt;p&gt;Nginx listens on &lt;strong&gt;Port 8080&lt;/strong&gt; and applies two simple routing rules to every incoming request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Rule 1: Route GitHub Webhooks → Probot GitHub App (Port 3000)&lt;/span&gt;
&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/api/webhook&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Rule 2: Route everything else → Heka Core Backend (Port 3001)&lt;/span&gt;
&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:3001&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In plain English:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If the request is to &lt;code&gt;/api/webhook&lt;/code&gt; → forward it to the GitHub App on Port 3000&lt;/li&gt;
&lt;li&gt;Everything else → forward it to the main Heka backend on Port 3001&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why Ngrok Is Necessary
&lt;/h3&gt;

&lt;p&gt;Here's the catch: Nginx only listens on localhost. But when GitHub fires a webhook event, "localhost" from GitHub's perspective means &lt;em&gt;GitHub's own servers&lt;/em&gt; — not your machine. It would send the event into the void, find nothing, and drop it silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ngrok&lt;/strong&gt; solves this by creating a public tunnel from your local Port 8080 directly to the internet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ngrok gives you a temporary public URL — something like &lt;code&gt;https://something.ngrok-free.app&lt;/code&gt;. You paste this into your GitHub App's webhook URL settings. Now the entire flow works end-to-end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub fires a PR event
        ↓
Hits the public Ngrok URL
        ↓
Ngrok tunnels it to Port 8080 on your local machine
        ↓
Nginx reads the request and applies the two routing rules
        ↓
Correct service handles it (Port 3000 or 3001)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A one-liner to remember the whole pipeline:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GitHub labels it and sends it → Ngrok blindly transports it → Nginx reads it and diverts it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Webhook Fixture Repository
&lt;/h2&gt;

&lt;p&gt;To avoid opening real pull requests every single time I want to test webhook handling, I created a dedicated fixture repository for generating and replaying PR events during development.&lt;/p&gt;

&lt;p&gt;You can find it here: &lt;a href="https://github.com/darshit2308/Heka-Webhook-Fixture" rel="noopener noreferrer"&gt;darshit2308/Heka-Webhook-Fixture&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The PR Slicing Plan
&lt;/h2&gt;

&lt;p&gt;To keep development incremental and mentor-reviewable, I've planned out the PR delivery sequence. This is tentative for now, but here's the rough order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ADRs and credential profile documentation&lt;/li&gt;
&lt;li&gt;Deterministic DID verification method helper and Hedera secret validation&lt;/li&gt;
&lt;li&gt;GPG challenge lifecycle module with tests&lt;/li&gt;
&lt;li&gt;GitHub App adapter skeleton and check-run creation &lt;em&gt;(separate repo from Heka, per mentor direction)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;GitHub OAuth and contributor binding&lt;/li&gt;
&lt;li&gt;Contributor credential OID4VCI support&lt;/li&gt;
&lt;li&gt;Heka Web Wallet prototype &lt;em&gt;(lives inside the Heka repository; non-Askar key management)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Heka Web UI onboarding flow&lt;/li&gt;
&lt;li&gt;Repository config parser and policy schema&lt;/li&gt;
&lt;li&gt;OID4VP verification integration &lt;em&gt;(Web Wallet as the primary target)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Lifecycle and recovery documentation &lt;em&gt;(runbooks; no revocation implementation)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;E2E test suite, docs, and final demo artifacts&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Looking Ahead
&lt;/h2&gt;

&lt;p&gt;Week 1 was heavy on planning, decisions, and setup — but that's exactly how it should be. A solid foundation now means fewer surprises (and fewer painful rewrites) later. Starting next week, the real implementation begins: ADRs, the DID helper utilities, and the first PR going out the door.&lt;/p&gt;

&lt;p&gt;Stay tuned for Week 2!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post is part of my ongoing LFDT Mentorship 2026 blog series. Feel free to reach out if you have questions or want to discuss anything covered here!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>blockchain</category>
      <category>devjournal</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Starting My LFX Mentorship Journey: Building Contributor Identity Verification for Hiero</title>
      <dc:creator>Darshit Khandelwal</dc:creator>
      <pubDate>Sun, 14 Jun 2026 19:25:09 +0000</pubDate>
      <link>https://dev.to/darshit2308/starting-my-lfx-mentorship-journey-building-contributor-identity-verification-for-hiero-583</link>
      <guid>https://dev.to/darshit2308/starting-my-lfx-mentorship-journey-building-contributor-identity-verification-for-hiero-583</guid>
      <description>&lt;p&gt;Today, June 15th, 2026, I officially start my 6-month LFX mentorship under the Linux Foundation Decentralized Trust (LFDT) program. I will be working on a project called Hiero Contributor Identity Verification Prototype, under the guidance of my mentor Alexander Shenshin.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;## What am I building, and why does it matter? *&lt;/em&gt;&lt;br&gt;
In open-source projects, anyone can open a pull request. But how does a project maintainer know that the person submitting code is actually who they claim to be? Right now, most projects simply trust GitHub usernames, which can be faked, compromised, or shared.&lt;br&gt;
This project solves that by building a real identity verification layer on top of GitHub's contribution workflow. When a contributor opens a pull request, instead of just trusting their username, the system cryptographically verifies their identity using:&lt;/p&gt;

&lt;p&gt;Decentralized Identifiers (DIDs) anchored on the Hedera blockchain&lt;br&gt;
Verifiable Credentials issued through the OID4VCI standard&lt;br&gt;
GPG cryptographic signatures tied to their GitHub account&lt;br&gt;
Verifiable Presentations verified at pull request time through OID4VP&lt;/p&gt;

&lt;p&gt;In simple terms, a contributor proves they are who they say they are, once, and that proof travels with every pull request they open, automatically. (There is another alternative, that is, instead of storing the proof, we can create the proof of user's validity on dynamically, after every Pull Request opened. Although, this is an open question and would be clarified by the end of week-1).&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;## What will I be documenting here? *&lt;/em&gt;&lt;br&gt;
I plan to post weekly updates throughout this mentorship. Each post will cover what I built that week, the architectural decisions I made and, more importantly, why I made them, and what I learned or found difficult.&lt;br&gt;
Specifically, I will be tracking:&lt;/p&gt;

&lt;p&gt;1) Weekly progress updates on implementation&lt;br&gt;
2) Architecture Decision Records explaining key technical choices&lt;br&gt;
3) Demo videos after every major milestone&lt;br&gt;
4) Honest reflections on blockers, mistakes, and lessons learned&lt;/p&gt;

&lt;p&gt;I have some experience working with decentralized identity standards like OID4VCI and OID4VP from my previous projects, so i will try to explain the concepts too in as much detail as possible, to help the community. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;## How can these posts be useful to you&lt;/strong&gt;&lt;br&gt;
These posts can be a genuine learning chance for individuals who are thinking to study concepts like DIDs, and dive into blockchain.&lt;br&gt;
If you are working on decentralized identity, Hedera, open-source tooling, or LFX mentorship yourself, you can follow along. &lt;/p&gt;

&lt;p&gt;Thanks a lot for reading until here!!&lt;br&gt;
We can connect here: &lt;br&gt;
linkedin: &lt;a href="https://www.linkedin.com/in/darshit-khandelwal-49bb25288" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/darshit-khandelwal-49bb25288&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/darshit2308" rel="noopener noreferrer"&gt;https://github.com/darshit2308&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>web3</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
