gitmark 0.0.68 → 0.0.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebSearch",
5
+ "WebFetch(domain:www.rgbfaq.com)",
6
+ "WebFetch(domain:rgb.info)",
7
+ "Bash(npm install)",
8
+ "Bash(npm search txo_parser)",
9
+ "Bash(../gitmark/bin/git-mark)",
10
+ "Bash(../gitmark/bin/git-mark --genesis abc123:0)"
11
+ ],
12
+ "deny": [],
13
+ "ask": [],
14
+ "defaultMode": "acceptEdits",
15
+ "additionalDirectories": [
16
+ "/home/melvin/remote/github.com/solidpayorg/solidpayorg"
17
+ ]
18
+ }
19
+ }
package/BURN.md ADDED
@@ -0,0 +1,11 @@
1
+ ## Introduction
2
+
3
+ Burn is an optional garbage collection operation. Burn is when you want to and a git mark tracking chain.
4
+
5
+ ## Transaction
6
+
7
+ The best way to do this is to send vout 0 back to the orginal pubkey, creating a tweak of 0 which is impossible.
8
+
9
+ ## Conclusion
10
+
11
+ This optional operation can clean up a chain and freeze it in time
package/CRYPTO.md ADDED
@@ -0,0 +1,27 @@
1
+ # Crypto
2
+
3
+ ![image](https://github.com/solidpayorg/gitmark/assets/65864/a06152fc-a7f5-4a99-a75f-4471e16ea173)
4
+
5
+ `version 0.0.2`
6
+
7
+ This page describes the on-chain primitives and mappings used in gitmark [SCHEMA](./SCHEMA.md)
8
+
9
+ # Gitmark Transitions: Bridging Blockchain and Git
10
+
11
+ Gitmark transitions offer a novel approach to embedding on-chain commitments within the blockchain, enabling a seamless capture of changes from one transaction to another. This methodology leverages a unique process termed as a **"tweaked spend to self"** to modify a transaction's output (TXO), enriching it with additional data while ensuring the original asset holder retains ownership.
12
+
13
+ ## How It Works
14
+
15
+ - **Initial State (Address 1)**: Identified as `A`, this represents the blockchain address prior to modification.
16
+ - **Transformed State (Address 2)**: Denoted as `A + T`, this address emerges post-modification, with `T` symbolizing the tweak applied to `A`. This tweak is not arbitrary; it encapsulates specific, transition-relevant information.
17
+
18
+ The new address has a vout of 0. All other vouts are not used in gitmark, they may be used to send funds elsewhere.
19
+
20
+ ### The Role of `T`
21
+
22
+ `T` stands out as it is meticulously designed to serve as an on-chain commitment, directly alluding to a distinct Git commit. The core of `T`'s value lies in its equivalence to the latest Git hash. This equivalence forges a verifiable nexus between the blockchain transaction and a specific snapshot of the Git repository.
23
+
24
+ ### Implications
25
+
26
+ This intricate connection ensures the blockchain's role extends beyond the realm of financial transactions, positioning it as a robust, unalterable ledger for software development benchmarks through Git commits. Gitmark transitions herald a new era of transparency and auditability in software development. They exploit blockchain's core strengths—trust and integrity—to solidify digital records' credibility.
27
+
package/GENESIS.md CHANGED
@@ -1,12 +1,16 @@
1
1
  ## Genesis
2
2
 
3
+ ### Prerequisites
4
+
3
5
  The genesis transaction output starts the chain of marks in git mark
4
6
 
5
- In order to start marking you will need a genesis transaction output
7
+ In order to start marking you will need a genesis transaction output aka uxto
6
8
 
7
9
  This will also have a public address and a private key (secret exponent)
8
10
 
9
- You will need the transaction id and the secret exponent to start git marking
11
+ You will need the **transaction output id** and the **private key** to start git marking
12
+
13
+ ### Storing Secrets
10
14
 
11
15
  One way to save the secret exponent is in git
12
16
 
@@ -14,7 +18,7 @@ One way to save the secret exponent is in git
14
18
  git config gitmark.secret <secretexponent>
15
19
  ```
16
20
 
17
- This is not terribly secure, but for small projects to get started it is convenient
21
+ This is not terribly secure, but for small projects to get started it is convenient. An article on trade offs of this approach is presented here [here](https://withblue.ink/2021/05/07/storing-secrets-and-passwords-in-git-is-bad.html)
18
22
 
19
23
  A useful way to generate an address and secret exponent would be:
20
24
 
@@ -22,28 +26,60 @@ https://project-bitmark.github.io/brain/
22
26
 
23
27
  Use a very secure password for anything more than testing
24
28
 
29
+ ### Funding
30
+
25
31
  Once you have an address, send some coins there from a faucet, a friend, or by being marked
26
32
 
27
33
  The genesis id also doubles as the @id for a gitmark project
28
34
 
29
- Optionally it can be added a file, `gitmark.json` in the root directory of your repo
35
+
36
+ ### Gitmark.json
37
+
38
+ Optionally a file can be added, `gitmark.json`, in the root directory of your repo
30
39
 
31
40
  It may look like this:
32
41
 
33
42
  ```JSON
34
43
  {
35
- "@id": "gitmark:b1fb9acb83f85887760b2e1a71e1df370976b1596be101bb0dbe8fd1c80f91cd:0",
36
- "genesis": "gitmark:b1fb9acb83f85887760b2e1a71e1df370976b1596be101bb0dbe8fd1c80f91cd:0",
44
+ "@id": "gitmark:59ff24db0321cb6b32a404815345b00ae68ca8b81150fbea6464ee10557e0fae:1",
45
+ "genesis": "gitmark:59ff24db0321cb6b32a404815345b00ae68ca8b81150fbea6464ee10557e0fae:1",
37
46
  "nick": "myrepo",
38
47
  "package": "./package.json",
48
+ "pubkey": "7574866ae7653e084c3c8e9e6359660e2c728d249fefb0f984bd32393f2ac67f",
39
49
  "repository": "./"
40
50
  }
41
51
  ```
42
52
 
53
+ - **@id** is the unspent transaction output
54
+ - **genesis** points to the same transaction output
55
+ - **nick** is a short name e.g. as used in npm
56
+ - **package** points to a package.json info
57
+ - **pubkey** is the pubkey for the genesis tx, in hex
58
+ - **repository** is an absolute or relative hint as to where to find the git code
59
+
60
+
61
+ ### First git mark
62
+
43
63
  Once you have your genesis tx, you can make your first git mark by running, for example:
44
64
 
45
65
  ```
46
66
  git mark --genesis b1fb9acb83f85887760b2e1a71e1df370976b1596be101bb0dbe8fd1c80f91cd:0
47
67
  ```
48
68
 
49
- Do this after you have commited your first files, and as recommended a gitmark.json file too
69
+ Do this after you have committed your first files, and as recommended a gitmark.json file too
70
+
71
+
72
+ ### Notes on Commits
73
+
74
+ The commit requires a name, email and commit message, with optional signing.
75
+
76
+ The **name** can be whatever you want
77
+
78
+ The **email** is open, but you can select a noreply address to indicate privacy, such as noreply@<genesis-hash>.gitmark
79
+
80
+ The **commit message** can be anything, and will be the first commit ready to be marked. e.g. "first"
81
+
82
+ ### Notes on .gitmark
83
+
84
+ The .gitmark address is just a place holder if it is undesirable to enter a public email address. In future it may be possible to resolve .gitmark URLs to a working GitHub repository and deployments
85
+
package/MARK.md ADDED
@@ -0,0 +1,38 @@
1
+ ![image](https://github.com/solidpayorg/gitmark/assets/65864/09e421be-a3ce-4e4d-b2c2-e5c1be4ebb87)
2
+
3
+ ## Introduction
4
+
5
+ Mark is the main function of gitmark. It has a prerequisite on [GENESIS](./GENESIS.md) and
6
+ with these two oprations you are able to create robust git repos recorded in time, and
7
+ identifiable globally.
8
+
9
+ ## Chain follows repo
10
+
11
+ A mark is a marking of the underlying block chain with an on-chain committment
12
+
13
+ The transaction is tweeked with the new git hash, according to [CRYPTO](./CRYPTO.md)
14
+
15
+ In essense, this proves the chain is following the github repo and provides a timestamp server
16
+
17
+ ## Repo follows chain
18
+
19
+ In order to create a two-way relationship the repo also follows the chain by recording the most recent
20
+ transaction object (txo) in a well known location.
21
+
22
+ ## TXO well known location
23
+
24
+ The txo well known location is in the directory `.txo`
25
+
26
+ In there is a file txo.txt which is of the form:
27
+
28
+ ```
29
+ <txo_uri> <balance>
30
+ ```
31
+
32
+ The balance is an amount in satoshis
33
+
34
+ ## Conclusion
35
+
36
+ In this way, the chain follows the repository, and the repository follows the chain,
37
+ leading to a robust, auditable, time-stamping solution using Git and blockchains.
38
+ This approach prevents double-spending by ensuring that the repository and the blockchain are in sync with each other.
package/OPS.md ADDED
@@ -0,0 +1,31 @@
1
+ ## Gitmark Operations
2
+
3
+ ### Genesis
4
+
5
+ Genesis is the utxo used to create a reference to the project
6
+
7
+ [Docs](./GENESIS.md)
8
+
9
+ ### Mark
10
+
11
+ Marks a super commit by tweaking a spend to self transaction by the by commit hash
12
+
13
+ [Docs](./MARK.md)
14
+
15
+ ### Topup
16
+
17
+ Tops up a seal, can be combined with Mark
18
+
19
+ [Docs](./MARK.md)
20
+
21
+ ### Transfer
22
+
23
+ Transfers ownership of the contract to another party
24
+
25
+ [Docs](./TRANSFER.md)
26
+
27
+ ### Burn
28
+
29
+ Marks a repo as inactive
30
+
31
+ [Docs](./BURN.md)
package/README.md CHANGED
@@ -1,159 +1,99 @@
1
- <div align="center">
2
- <h1>gitmark</h1>
3
- </div>
1
+ # git mark
4
2
 
5
- <div align="center">
6
- Mark your git commits, to create global consensus, and a definitive project history
7
- </div>
3
+ Anchor git commits to Bitcoin via [blocktrails](https://blocktrails.org) key chaining.
8
4
 
9
- ---
5
+ [![npm](https://img.shields.io/npm/v/gitmark)](https://npmjs.com/package/gitmark)
6
+ [![license](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
10
7
 
11
- <div align="center">
12
- <h4>Getting Started</h4>
13
- </div>
14
-
15
- ---
16
-
17
-
18
- [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/solidpayorg/git-mark/blob/gh-pages/LICENSE)
19
- ![npm](https://img.shields.io/npm/v/gitmark)
20
- [![npm](https://img.shields.io/npm/dw/gitmark.svg)](https://npmjs.com/package/gitmark)
21
- [![Github Stars](https://img.shields.io/github/stars/solidpayorg/gitmark.svg)](https://github.com/solidpayorg/gitmark/)
22
-
23
-
24
- ## Introduction
25
-
26
- Gitmark extends git to allow commits to be "marked" (using the command `git mark`) by a supporting block chain. This "reinforces" or "finalizes" a given commit, to determine global consensus and a definitive project history
27
-
28
- ## Installation
8
+ ## Install
29
9
 
30
10
  ```sh
31
- sudo npm install -g gitmark
11
+ npm install -g gitmark
32
12
  ```
33
13
 
34
- ## Usage
35
-
36
- ```bash
37
- git mark # marks the current commit
38
-
39
- git mark [--genesis txoutput] # used for the genesis commit
40
- ```
41
-
42
- ## Motivation
43
-
44
- Gitmark, was originally created to facilitate the [marking](https://github.com/project-bitmark/marking/wiki) use case, which aims to allow global, distributed, reputation trees, to be grounded in a block chain
45
-
46
- What is made possible, is a way to provide consensus on a definitive git branch/chain, in order to ensure that the history has not been tampered with
47
-
48
- One can reconstruct the current state from the history, and this can also be used to preserve your reputation at any given time, say, if any provider ceases to operate
49
-
50
- It's also possible to audit and verify the integrity of the git chain, to create a secure, finalized, state machine, with a definitive head, that is globally synced
51
-
52
- The system can be extended beyond reputation trees, to use any git based store, and anchor it to a secure, verifyable chain of blocks, to determine the definitve history
53
-
54
- Many thanks go to Peter Todd for his work on [single use seals](https://petertodd.org/2017/scalable-single-use-seal-asset-transfer) and Dr Maxim Orlovsky for his work on [RGB](https://rgb-org.github.io/)
55
-
56
- _Gitmark is pre-alpha software, it should be considered experimental, and used at your own risk_
57
-
58
- ## Prerequisites
59
-
60
- Because gitmark was designed to anchor reputation trees, the reputation of the underlying block chain must be unimpared. Gitmark only supports block chains that are provably fair. Bitcoin is regarded as the most secure and fairest of all block chains, and should be used for high value projects where cost is not an issue
61
-
62
- Gitmark does not support projects that are premines, instamines, ICOs, have developer taxes or provably unfair consensus, such as proof of stake
63
-
64
- In solving the reputation use case, we aim to innovate in the space, contribute back code, and operate as a testing ground
65
-
66
- Bearing in mind that this is experimental software, the [bitmark](https://bitmark.rocks/) block chain, is the first chain on which gitmark is tested and implemented. As it inexpensive, easily obitained, and was designed for the grounding of reputation trees (gitmark is a play on the word bitmark). This also provides a testing ground for developers to get started. The Liquid network is also a possible target, and Litecoin appears to be another good possibility, as well as the various testnets
67
-
68
- The first prerequisite is to obtain an unspent transaction on a supporting block chain. This can be in any coin, but to get started we suggest, getting hold of one Bitmark, which can be obtained inexpensively for example in the [chat room](https://projectbitmark.slack.com/) as it is designed to be spread between helpful actors, to foster innovation
69
-
70
- ## Getting started
14
+ This gives you `git mark` as a native git subcommand.
71
15
 
72
- After you have obtained some block chain currency, send those coins to an address for which you have the keypair. That becomes the genesis unspent transaction
16
+ ## Quick Start
73
17
 
74
- Having created a genesis transaction, and recording the key pair safely, you are ready to start marking!
75
-
76
- Install gitmark globally via npm, the executables will be in the bin directory which can be located with `which git-mark`
77
-
78
- The genesis transaction provides the first input to the `git mark` command, and the private key (secret exponent in hex) is needed to advance the genesis transaction in line with the current git HEAD
79
-
80
- ## Git mark
81
-
82
- Simply running `git mark` on a repo will create your first marking
83
-
84
- You will need a genesis address for the first run of the form `tx:output` and supply that with the argument --genesis [tx]
85
-
86
- You will also need the private key from that tx, which is an argument to the gitmark script, but can be also saved in a location as directed by the output from the git mark command. The key (ie 64 char hex secret exponent) should be stored in the indicated file with the json key "privkey"
87
-
88
- _Warning: do not use the default private key, that is set, in the script!_
89
-
90
- Git mark will generate a new address to send to, a fee, an amount, a spending private key and unspent tx data as inputs to an rpc or a simple script `tx.sh` that lives in the bin directory. Future versions will use a transaction builder to send to a network directly
91
-
92
- After running this script, an empty commit message is generated which you can check in, and points to the latest new unspent transaction, creating a two way link. The commit message is a gitmark [URI](./URI.md)
93
-
94
- Congratulations! You have now marked your first git repo!
95
-
96
- See also: [Example Workflow](./WORKFLOW.md)
97
-
98
- ## How it works
99
-
100
- Gitmark simply uses [single use seals](https://petertodd.org/2017/scalable-single-use-seal-asset-transfer) to tweak the initial public key address of the genesis transaction by the commit hash of the git tree. The current git hash is added to the original, genesis, public key in the output transaction, creating a chain of commits in the block chain
101
-
102
- In this way the block chain points to git. It then points the next commit back to the block chain tx, creating two way link, and therefore, strong binding, at one particular point in time
18
+ ```bash
19
+ # Initialize in a git repo (tbtc4 = Bitcoin testnet4)
20
+ git mark init --chain tbtc4 --voucher txo:tbtc4:txid:vout?amount=X&key=Y
103
21
 
104
- Similarly the definitive git tree forms a chain of commits that go forward in time, and so do the new transaction on the block chain. Further commits are periodically marked in time proving an auditable trail in both time on-chain of the evolution of the git tree. It also shows the latest confirmed state of a git tree that can be used for trading or in safe or smart contracts
22
+ # Make commits, then mark them
23
+ git commit -m "my change"
24
+ git mark
105
25
 
106
- The first use case for gitmark is marking of reputation trees, but it can be applied to any git system where the history is important
26
+ # Verify the trail against Bitcoin
27
+ git mark verify
107
28
 
108
- ## Recent git marks
29
+ # Show trail state
30
+ git mark info
31
+ ```
109
32
 
110
- - [Github](https://github.com/search?o=desc&q=%22gitmark+%22&s=committer-date&type=Commits)
33
+ ## How It Works
111
34
 
112
- ## Use Cases
35
+ Each `git mark` creates a real Bitcoin transaction. The address is derived from your key + the commit hash using BIP-341 taproot key chaining:
113
36
 
114
- - Distributed Reputation Trees
115
- - Distributed Ledgers
116
- - Registries
117
- - Safe or Smart Contracts
118
- - Asset Issuance
119
- - Distributed Exchanges
120
- - Reconstruct histories from Genesis
121
- - Distributed Global Consensus
122
- - Domain independent web sites
123
- - Archiving and Time Travel through History
124
- - Distributed Identity and PKI
125
- - Federated Side Chains
126
- - Auditing Histories
127
- - Fraud Detection
128
- - Supply Chains
37
+ ```
38
+ baseKey + tweak(commit₁) → Address₁ → TXO₁
39
+ baseKey + tweak(commit₁, commit₂) → Address₂ → TXO₂
40
+ ```
129
41
 
130
- ## Related work
42
+ The chain of addresses on Bitcoin mirrors the chain of commits in git. Anyone can verify by re-deriving the addresses from the public key + commit hashes.
43
+
44
+ ## Commands
45
+
46
+ | Command | Description |
47
+ |---------|-------------|
48
+ | `git mark init` | Initialize trail. Options: `--chain`, `--voucher`, `--force` |
49
+ | `git mark` | Anchor HEAD commit to Bitcoin |
50
+ | `git mark info` | Show trail state, balance, addresses |
51
+ | `git mark verify` | Verify all marks against Bitcoin |
52
+
53
+ ## Trail File
54
+
55
+ `blocktrails.json` in your repo root — committed, visible, verifiable:
56
+
57
+ ```json
58
+ {
59
+ "version": "0.0.3",
60
+ "profile": "gitmark",
61
+ "publicKeyBase": "02abc...",
62
+ "chain": "tbtc4",
63
+ "states": ["a1b2c3", "e5f6a7"],
64
+ "txo": [
65
+ "txo:tbtc4:abc:0?commit=a1b2c3",
66
+ "txo:tbtc4:def:0?commit=e5f6a7"
67
+ ]
68
+ }
69
+ ```
131
70
 
132
- - [Single Use Seals](https://petertodd.org/2017/scalable-single-use-seal-asset-transfer)
133
- - [RGB](https://rgb-org.github.io/)
134
- - [Commerce Block Mainstay](https://www.commerceblock.com/mainstay/) [[White Paper](https://cloudflare-ipfs.com/ipns/ipfs.commerceblock.com/commerceblock-whitepaper-mainstay.pdf)]
135
- - [BIP 175 - Pay to Contract Protocol](https://github.com/bitcoin/bips/blob/master/bip-0175.mediawiki)
71
+ - **publicKeyBase** compressed pubkey (02/03 prefix), base for BIP-341 key chaining
72
+ - **states** — commit hashes (input to key derivation)
73
+ - **txo** Bitcoin anchors (TXO URIs, self-contained and verifiable)
136
74
 
137
- ## Source code
75
+ Private spending state stored in `.git/blocktrails.json` (never committed).
138
76
 
139
- - [Source](https://github.com/solidpayorg/gitmark)
140
- - [Issue Tracker](https://github.com/solidpayorg/gitmark/issues)
141
- - [NPM](https://www.npmjs.com/package/gitmark)
77
+ ## Key Storage
142
78
 
143
- ## Future work
79
+ Your private key is stored in git config:
144
80
 
145
- - Git marks can be extended to further layers by using git submodules hence creating almost unlimited space
81
+ ```bash
82
+ git config nostr.privkey <64-char-hex>
83
+ ```
146
84
 
147
- - Private git repositories can be supported out of the box, and given that private keys are used in each seal, encrypted backups can be made
85
+ Same secp256k1 key used for Nostr, Bitcoin, and Solid pod authentication.
148
86
 
149
- - The project git tree can be backed up or archived using git clone in multiple locations. It is natural that popular projects are cloned often in any case
87
+ ## Dependencies
150
88
 
151
- - Seals can be opened and closed using a federation, in order to try out multiple consensus and vefification methods
89
+ Two: `@noble/curves` and `@noble/hashes`. No bitcoinjs-lib, no external transaction builders.
152
90
 
153
- - More robust verification frameworks can be built using node testing frameworks, and continusous integration, tho currently the distribution contains a git-mark-verify script
91
+ ## Related
154
92
 
155
- - Lightweight Autonomous Marking Agents (LAMAs) can be created that listen to communities for marks and just work, without needing a human operator. The service can be deployed on a server or container, and be designed to bring itself up if it goes down in any one location
93
+ - [blocktrails.org](https://blocktrails.org) state anchoring on Bitcoin
94
+ - [git-mark.com](https://git-mark.com) — project homepage
95
+ - [BIP-341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki) — Taproot key tweaking
156
96
 
157
97
  ## License
158
98
 
159
- - MIT
99
+ MIT
package/SCHEMA.md ADDED
@@ -0,0 +1,25 @@
1
+ ## Gitmark Schema
2
+
3
+ The Gitmark schema is a framework for organizing and tracking changes in a Git repository. It is inspired by the concept of [RGB Schemas](https://www.rgbfaq.com/glossary/schema-and-scripts/schema), which provide a way to represent and validate data in a decentralized network.
4
+
5
+ ## Operations (OPS)
6
+
7
+ The Gitmark schema defines five core operations (OPS) for managing the state of a Git repository:
8
+
9
+ 1. [GENESIS](./GENESIS.md): Initializes a new Git repository and creates the first commit.
10
+ 2. [MARK](./MARK.md): Records a specific state or event in the Git repository, such as a milestone or release.
11
+ 3. [TOPUP](./TOPUP.md): Adds new funds or resources to the Git repository, such as through a crowdfunding campaign or sponsorship.
12
+ 4. [TRANSFER](./TRANSFER.md): Moves funds or resources from one part of the Git repository to another, such as between different branches or accounts.
13
+ 5. [BURN](./BURN.md): Closes out a Git repository and transfers any remaining funds or resources to a designated recipient.
14
+
15
+ ## Cryptographic Operations
16
+
17
+ The Gitmark schema uses cryptographic operations to ensure the integrity and security of the Git repository. These operations include:
18
+
19
+ 1. Tweaking: A process for transitioning from one state to another in the Git repository, using cryptographic techniques to ensure that the transition is valid and secure.
20
+ 2. Genesis: The creation of a new Git repository, using cryptographic techniques to establish the initial state and ensure that it is secure.
21
+ 3. And other cryptographic operations as described in [CRYPTO](./CRYPTO.md)
22
+
23
+ The Gitmark schema maps each of these cryptographic operations to the corresponding OPS, ensuring that the Git repository is managed in a secure and consistent manner.
24
+
25
+ By following the Gitmark schema, developers can ensure that their Git repositories are well-organized, secure, and easy to manage. The schema provides a clear framework for tracking changes and managing resources, making it easier to collaborate and build high-quality software.
package/TOPUP.md ADDED
@@ -0,0 +1,15 @@
1
+ ## Introduction
2
+
3
+ Topup is a function of Gitmark that lets you increase the amount of funds in a commitment. This allows a repo to continue operating after it has become low on funds.
4
+
5
+ ## Add another tx input
6
+
7
+ Simply add another tx input to your transaction, and the new transaction output will have more funds in it.
8
+
9
+ ## Donations
10
+
11
+ Certain versions of the software may incur donations; this, in turn, could be used to top up the project and keep it going longer.
12
+
13
+ ## Conclusion
14
+
15
+ Topup is a super simple workflow that sweeps inputs into a new output spliced together with a mark, in order to increase funds on-chain.
package/USE_CASES.md ADDED
@@ -0,0 +1,67 @@
1
+ # **Gitmark Use Cases**
2
+
3
+ Gitmark harnesses git and blockchain technologies to provide innovative solutions across various fields. Below is a detailed breakdown of its diverse applications:
4
+
5
+ ### **1. Distributed Reputation Systems**
6
+ - **Purpose:** Create tamper-proof, transparent, decentralized reputation systems.
7
+ - **Benefits:** Enhances trust in digital interactions.
8
+
9
+ ### **2. Distributed Ledgers**
10
+ - **Applications:** Financial transactions, supply chain management, etc.
11
+ - **Technology:** Combines git with blockchain for secure, decentralized ledgers.
12
+
13
+ ### **3. Registries**
14
+ - **Function:** Establish decentralized registries for assets, identities, and more.
15
+ - **Advantages:** Ensures data integrity and accessibility.
16
+
17
+ ### **4. Safe or Smart Contracts**
18
+ - **Capability:** Execute contracts stored on blockchain, ensuring transparency and immutability.
19
+ - **Feature:** Automated contract execution.
20
+
21
+ ### **5. Asset Issuance**
22
+ - **Service:** Issue digital assets like tokens or certificates with a blockchain-backed history.
23
+ - **Highlight:** Provides verifiability and auditability.
24
+
25
+ ### **6. Distributed Exchanges**
26
+ - **Description:** Build decentralized exchanges for peer-to-peer asset trading.
27
+ - **Characteristic:** Maintains a transparent and immutable transaction record.
28
+
29
+ ### **7. Historical Reconstruction**
30
+ - **Functionality:** Reconstruct complete histories from genesis block.
31
+ - **Use:** Offers a comprehensive, verifiable record of transactions and changes.
32
+
33
+ ### **8. Distributed Global Consensus**
34
+ - **Purpose:** Facilitate decentralized decision-making and governance.
35
+ - **Approach:** Enables distributed consensus on system states.
36
+
37
+ ### **9. Decentralized Web Hosting**
38
+ - **Application:** Host websites in a decentralized manner, promoting resistance to censorship.
39
+ - **Benefit:** Eliminates single points of failure.
40
+
41
+ ### **10. Archiving and Historical Analysis**
42
+ - **Features:** Archive data and navigate through historical records.
43
+ - **Use Case:** Provides insights and supports audits.
44
+
45
+ ### **11. Distributed Identity and PKI**
46
+ - **Function:** Create decentralized identity systems and public key infrastructure.
47
+ - **Advantages:** Enhances security and privacy in digital interactions.
48
+
49
+ ### **12. Federated Side Chains**
50
+ - **Objective:** Build interoperable side chains among different blockchains or git repositories.
51
+ - **Benefit:** Facilitates communication and interoperability.
52
+
53
+ ### **13. Auditing**
54
+ - **Capability:** Audit entire histories of systems for transparency and accountability.
55
+ - **Contexts:** Useful in financial transactions, supply chain management, etc.
56
+
57
+ ### **14. Fraud Detection**
58
+ - **Method:** Maintain a tamper-proof, auditable record to detect and prevent fraud.
59
+ - **Industries:** Applicable across various sectors.
60
+
61
+ ### **15. Supply Chain Management**
62
+ - **Service:** Create traceable and accountable supply chain systems.
63
+ - **Feature:** Enhances transparency and efficiency throughout processes.
64
+
65
+ ### **Conclusion**
66
+ Gitmark's integration of git and blockchain technologies offers transformative potential across industries, revolutionizing data storage, sharing, and verification to create more secure and efficient systems.
67
+
@@ -0,0 +1,456 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * git-mark — Anchor git commits to Bitcoin via blocktrails
5
+ *
6
+ * Usage:
7
+ * git mark init [--chain tbtc4] [--voucher txo:...]
8
+ * git mark [--chain tbtc4]
9
+ * git mark info
10
+ * git mark verify
11
+ */
12
+
13
+ import { secp256k1, schnorr } from '@noble/curves/secp256k1';
14
+ import { sha256 } from '@noble/hashes/sha256';
15
+ import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
16
+ import { execSync } from 'child_process';
17
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
18
+ import { join } from 'path';
19
+
20
+ // --- Constants ---
21
+ const SECP_N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141n;
22
+ const TRAIL_FILE = 'blocktrails.json';
23
+ const PRIVATE_FILE = '.git/blocktrails.json';
24
+ const DEFAULT_CHAIN = 'tbtc4';
25
+
26
+ const CHAINS = {
27
+ tbtc3: { explorer: 'https://mempool.space/testnet/api', name: 'Bitcoin Testnet3' },
28
+ tbtc4: { explorer: 'https://mempool.space/testnet4/api', name: 'Bitcoin Testnet4' },
29
+ btc: { explorer: 'https://mempool.space/api', name: 'Bitcoin' },
30
+ signet: { explorer: 'https://mempool.space/signet/api', name: 'Bitcoin Signet' },
31
+ };
32
+
33
+ // --- Blocktrails key chaining (BIP-341) ---
34
+ function taggedHash(tag, ...msgs) {
35
+ const tagHash = sha256(new TextEncoder().encode(tag));
36
+ return sha256(concatBytes(tagHash, tagHash, ...msgs));
37
+ }
38
+
39
+ function btScalar(pubkeyCompressed, state) {
40
+ const xOnly = pubkeyCompressed.slice(1);
41
+ const stateBytes = typeof state === 'string' ? new TextEncoder().encode(state) : state;
42
+ const sh = sha256(stateBytes);
43
+ return bytesToBigInt(taggedHash('TapTweak', xOnly, sh)) % SECP_N;
44
+ }
45
+
46
+ function bytesToBigInt(bytes) {
47
+ return BigInt('0x' + bytesToHex(bytes));
48
+ }
49
+
50
+ function bigIntToBytes(n) {
51
+ const hex = n.toString(16).padStart(64, '0');
52
+ return hexToBytes(hex);
53
+ }
54
+
55
+ function deriveChainedPrivkey(privkeyBytes, states) {
56
+ let d = bytesToBigInt(privkeyBytes);
57
+ let cur = new Uint8Array(secp256k1.getPublicKey(privkeyBytes, true));
58
+ for (const s of states) {
59
+ const t = btScalar(cur, s);
60
+ d = (d + t) % SECP_N;
61
+ cur = new Uint8Array(secp256k1.ProjectivePoint.BASE.multiply(d).toRawBytes(true));
62
+ }
63
+ return bigIntToBytes(d);
64
+ }
65
+
66
+ function deriveChainedPubkey(pubkeyBase, states) {
67
+ let P = secp256k1.ProjectivePoint.fromHex(bytesToHex(pubkeyBase));
68
+ let cur = pubkeyBase;
69
+ for (const s of states) {
70
+ const t = btScalar(cur, s);
71
+ P = P.add(secp256k1.ProjectivePoint.BASE.multiply(t));
72
+ cur = new Uint8Array(P.toRawBytes(true));
73
+ }
74
+ return cur;
75
+ }
76
+
77
+ // --- Bech32m encoding ---
78
+ const BECH32_CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
79
+ function polymod(values) {
80
+ const GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
81
+ let chk = 1;
82
+ for (const v of values) { const b = chk >> 25; chk = ((chk & 0x1ffffff) << 5) ^ v; for (let i = 0; i < 5; i++) if ((b >> i) & 1) chk ^= GEN[i]; }
83
+ return chk;
84
+ }
85
+ function hrpExpand(hrp) { const r = []; for (const c of hrp) r.push(c.charCodeAt(0) >> 5); r.push(0); for (const c of hrp) r.push(c.charCodeAt(0) & 31); return r; }
86
+ function convertBits(data, from, to) { let acc = 0, bits = 0; const r = []; const max = (1 << to) - 1; for (const v of data) { acc = (acc << from) | v; bits += from; while (bits >= to) { bits -= to; r.push((acc >> bits) & max); } } if (bits > 0) r.push((acc << (to - bits)) & max); return r; }
87
+
88
+ function bech32mEncode(hrp, version, program) {
89
+ const values = [version, ...convertBits(program, 8, 5)];
90
+ const enc = [...hrpExpand(hrp), ...values, 0, 0, 0, 0, 0, 0];
91
+ const mod = polymod(enc) ^ 0x2bc830a3;
92
+ const checksum = [0, 1, 2, 3, 4, 5].map(i => (mod >> (5 * (5 - i))) & 31);
93
+ let result = hrp + '1';
94
+ for (const v of [...values, ...checksum]) result += BECH32_CHARSET[v];
95
+ return result;
96
+ }
97
+
98
+ function pubkeyToAddress(pubkeyHex, states, chain) {
99
+ const pubBytes = hexToBytes(pubkeyHex);
100
+ const derived = states.length > 0 ? deriveChainedPubkey(pubBytes, states) : pubBytes;
101
+ const xOnly = derived.slice(1);
102
+ const hrp = chain === 'btc' ? 'bc' : 'tb';
103
+ return bech32mEncode(hrp, 1, xOnly);
104
+ }
105
+
106
+ // --- P2TR script ---
107
+ function p2trScript(xonly) {
108
+ return new Uint8Array([0x51, 0x20, ...xonly]);
109
+ }
110
+
111
+ // --- Transaction building helpers ---
112
+ function writeVarInt(n) {
113
+ if (n < 0xfd) return new Uint8Array([n]);
114
+ if (n <= 0xffff) { const b = new Uint8Array(3); b[0] = 0xfd; b[1] = n & 0xff; b[2] = (n >> 8) & 0xff; return b; }
115
+ return new Uint8Array([0xfe, n & 0xff, (n >> 8) & 0xff, (n >> 16) & 0xff, (n >> 24) & 0xff]);
116
+ }
117
+ function writeU32LE(n) { const b = new Uint8Array(4); b[0] = n & 0xff; b[1] = (n >> 8) & 0xff; b[2] = (n >> 16) & 0xff; b[3] = (n >> 24) & 0xff; return b; }
118
+ function writeU64LE(n) { const b = new Uint8Array(8); let v = BigInt(n); for (let i = 0; i < 8; i++) { b[i] = Number(v & 0xffn); v >>= 8n; } return b; }
119
+ function concatBytes(...arrays) { const r = new Uint8Array(arrays.reduce((s, a) => s + a.length, 0)); let o = 0; for (const a of arrays) { r.set(a, o); o += a.length; } return r; }
120
+ function reverseTxid(txid) { return hexToBytes(txid).reverse(); }
121
+
122
+ function buildTransaction(input, outputs, privkeyBytes) {
123
+ const inputs = [input];
124
+ const internalXOnly = new Uint8Array(secp256k1.getPublicKey(privkeyBytes, true)).slice(1);
125
+ const untweakedHex = '5120' + bytesToHex(internalXOnly);
126
+ const needsTweak = bytesToHex(inputs[0].scriptPubKey) !== untweakedHex;
127
+ let signingKey = privkeyBytes;
128
+ if (needsTweak) {
129
+ const tweak = taggedHash('TapTweak', internalXOnly);
130
+ const t = bytesToBigInt(tweak);
131
+ let d = bytesToBigInt(privkeyBytes);
132
+ const fullPub = secp256k1.getPublicKey(privkeyBytes, false);
133
+ if (fullPub[64] & 1) d = SECP_N - d;
134
+ signingKey = bigIntToBytes((d + t) % SECP_N);
135
+ }
136
+
137
+ const version = 2, locktime = 0, sequence = 0xfffffffd;
138
+ const serOutputs = outputs.map(o =>
139
+ concatBytes(writeU64LE(o.amount), writeVarInt(o.scriptPubKey.length), o.scriptPubKey)
140
+ );
141
+
142
+ const shaPrevouts = sha256(concatBytes(...inputs.map(i => concatBytes(reverseTxid(i.txid), writeU32LE(i.vout)))));
143
+ const shaAmounts = sha256(concatBytes(...inputs.map(i => writeU64LE(i.amount))));
144
+ const shaScriptPubKeys = sha256(concatBytes(...inputs.map(i => concatBytes(writeVarInt(i.scriptPubKey.length), i.scriptPubKey))));
145
+ const shaSequences = sha256(concatBytes(...inputs.map(() => writeU32LE(sequence))));
146
+ const shaOutputs = sha256(concatBytes(...serOutputs));
147
+
148
+ const sigs = [];
149
+ for (let i = 0; i < inputs.length; i++) {
150
+ const sigMsg = concatBytes(
151
+ new Uint8Array([0x00, 0x00]),
152
+ writeU32LE(version), writeU32LE(locktime),
153
+ shaPrevouts, shaAmounts, shaScriptPubKeys, shaSequences, shaOutputs,
154
+ new Uint8Array([0x00]),
155
+ writeU32LE(i)
156
+ );
157
+ const sighash = taggedHash('TapSighash', sigMsg);
158
+ sigs.push(schnorr.sign(sighash, signingKey));
159
+ }
160
+
161
+ const parts = [
162
+ writeU32LE(version),
163
+ new Uint8Array([0x00, 0x01]),
164
+ writeVarInt(inputs.length)
165
+ ];
166
+ for (const inp of inputs) {
167
+ parts.push(reverseTxid(inp.txid), writeU32LE(inp.vout), new Uint8Array([0x00]), writeU32LE(sequence));
168
+ }
169
+ parts.push(writeVarInt(outputs.length));
170
+ for (const so of serOutputs) parts.push(so);
171
+ for (const sig of sigs) {
172
+ parts.push(new Uint8Array([0x01]), writeVarInt(sig.length), sig);
173
+ }
174
+ parts.push(writeU32LE(locktime));
175
+ return bytesToHex(concatBytes(...parts));
176
+ }
177
+
178
+ async function broadcastTx(rawHex, explorer) {
179
+ const resp = await fetch(`${explorer}/tx`, { method: 'POST', body: rawHex, headers: { 'Content-Type': 'text/plain' } });
180
+ if (!resp.ok) throw new Error(`Broadcast failed: ${await resp.text()}`);
181
+ return (await resp.text()).trim();
182
+ }
183
+
184
+ // --- Git helpers ---
185
+ function gitExec(cmd) { return execSync(cmd, { encoding: 'utf8' }).trim(); }
186
+ function getHead() { return gitExec('git rev-parse HEAD'); }
187
+ function getPrivkey() {
188
+ try { return gitExec('git config nostr.privkey'); } catch { return null; }
189
+ }
190
+ function setPrivkey(key) { gitExec(`git config --local nostr.privkey ${key}`); }
191
+ function isGitRoot() { return existsSync('.git'); }
192
+
193
+ // --- Trail file helpers ---
194
+ function loadTrail() {
195
+ if (!existsSync(TRAIL_FILE)) return null;
196
+ return JSON.parse(readFileSync(TRAIL_FILE, 'utf8'));
197
+ }
198
+ function saveTrail(trail) {
199
+ writeFileSync(TRAIL_FILE, JSON.stringify(trail, null, 2) + '\n');
200
+ }
201
+ function loadPrivateState() {
202
+ if (!existsSync(PRIVATE_FILE)) return null;
203
+ return JSON.parse(readFileSync(PRIVATE_FILE, 'utf8'));
204
+ }
205
+ function savePrivateState(state) {
206
+ writeFileSync(PRIVATE_FILE, JSON.stringify(state, null, 2) + '\n');
207
+ }
208
+
209
+ // --- Parse TXO URI ---
210
+ function parseTxoUri(uri) {
211
+ const match = uri.match(/^txo:([^:]+):([a-f0-9]+):(\d+)/);
212
+ if (!match) throw new Error('Invalid TXO URI');
213
+ const params = new URLSearchParams(uri.split('?')[1] || '');
214
+ return { chain: match[1], txid: match[2], vout: parseInt(match[3]), amount: parseInt(params.get('amount')), key: params.get('key') };
215
+ }
216
+
217
+ // --- Commands ---
218
+ async function cmdInit(args) {
219
+ if (!isGitRoot()) {
220
+ console.error('Error: .git/ not found in current directory. Run from a git repo root.');
221
+ process.exit(1);
222
+ }
223
+ if (existsSync(TRAIL_FILE) && !args.includes('--force')) {
224
+ console.error(`Error: ${TRAIL_FILE} already exists. Use --force to reinitialize.`);
225
+ process.exit(1);
226
+ }
227
+
228
+ // Chain
229
+ const chainIdx = args.indexOf('--chain');
230
+ const chain = chainIdx !== -1 ? args[chainIdx + 1] : DEFAULT_CHAIN;
231
+ if (!CHAINS[chain]) { console.error(`Unknown chain: ${chain}`); process.exit(1); }
232
+
233
+ // Key
234
+ let privkey = getPrivkey();
235
+ let keySource = 'git config';
236
+ if (!privkey) {
237
+ const sk = secp256k1.utils.randomPrivateKey();
238
+ privkey = bytesToHex(sk);
239
+ setPrivkey(privkey);
240
+ keySource = 'generated';
241
+ }
242
+ const pubkey = bytesToHex(secp256k1.getPublicKey(hexToBytes(privkey), true));
243
+
244
+ // Create trail
245
+ const trail = {
246
+ version: '0.0.3',
247
+ profile: 'gitmark',
248
+ publicKeyBase: pubkey,
249
+ chain,
250
+ states: [],
251
+ txo: []
252
+ };
253
+
254
+ // Voucher funding
255
+ const voucherIdx = args.indexOf('--voucher');
256
+ if (voucherIdx !== -1) {
257
+ const voucherUri = args[voucherIdx + 1];
258
+ const txo = parseTxoUri(voucherUri);
259
+ if (!txo.key) { console.error('Voucher must include &key= parameter'); process.exit(1); }
260
+ if (!txo.amount) { console.error('Voucher must include &amount= parameter'); process.exit(1); }
261
+
262
+ const voucherKey = hexToBytes(txo.key);
263
+ const baseXonly = hexToBytes(pubkey).slice(1);
264
+ const baseScript = p2trScript(baseXonly);
265
+
266
+ // Fetch voucher scriptPubKey
267
+ const explorer = CHAINS[chain].explorer;
268
+ const txResp = await fetch(`${explorer}/tx/${txo.txid}`);
269
+ if (!txResp.ok) { console.error('Could not fetch voucher transaction'); process.exit(1); }
270
+ const txData = await txResp.json();
271
+ const prevOut = txData.vout?.[txo.vout];
272
+ if (!prevOut) { console.error(`Voucher output ${txo.vout} not found`); process.exit(1); }
273
+
274
+ const fee = 300;
275
+ const outputAmount = txo.amount - fee;
276
+ if (outputAmount <= 546) { console.error('Voucher too small'); process.exit(1); }
277
+
278
+ const rawTx = buildTransaction(
279
+ { txid: txo.txid, vout: txo.vout, amount: txo.amount, scriptPubKey: hexToBytes(prevOut.scriptpubkey) },
280
+ [{ amount: outputAmount, scriptPubKey: baseScript }],
281
+ voucherKey
282
+ );
283
+ const newTxid = await broadcastTx(rawTx, explorer);
284
+
285
+ savePrivateState({ txid: newTxid, vout: 0, amount: outputAmount });
286
+ console.log(`Funded: ${outputAmount} sats (txid: ${newTxid})`);
287
+ }
288
+
289
+ saveTrail(trail);
290
+
291
+ console.log(`Initialized gitmark trail: ${TRAIL_FILE}`);
292
+ console.log(`Key source: ${keySource}`);
293
+ console.log(`Base public key: ${pubkey}`);
294
+ console.log(`Chain: ${chain}`);
295
+ console.log(`Address: ${pubkeyToAddress(pubkey, [], chain)}`);
296
+ if (!existsSync(PRIVATE_FILE) && voucherIdx === -1) {
297
+ console.log(`\nUnfunded. Use: git mark init --voucher txo:${chain}:txid:vout?amount=X&key=Y`);
298
+ console.log(`Or send sats to: ${pubkeyToAddress(pubkey, [], chain)}`);
299
+ }
300
+ }
301
+
302
+ async function cmdMark(args) {
303
+ const trail = loadTrail();
304
+ if (!trail) { console.error(`No ${TRAIL_FILE} found. Run: git mark init`); process.exit(1); }
305
+ const priv = loadPrivateState();
306
+ if (!priv) { console.error('No funding. Run: git mark init --voucher txo:...'); process.exit(1); }
307
+
308
+ const privkey = getPrivkey();
309
+ if (!privkey) { console.error('No private key. Set: git config nostr.privkey <hex>'); process.exit(1); }
310
+
311
+ const head = getHead();
312
+ const chain = trail.chain;
313
+ const explorer = CHAINS[chain]?.explorer;
314
+ if (!explorer) { console.error(`Unknown chain: ${chain}`); process.exit(1); }
315
+
316
+ // All previous states + current commit
317
+ const prevStates = [...trail.states];
318
+ const allStates = [...prevStates, head];
319
+
320
+ // Derive signing key (chained through previous states)
321
+ const signingKey = prevStates.length > 0
322
+ ? deriveChainedPrivkey(hexToBytes(privkey), prevStates)
323
+ : hexToBytes(privkey);
324
+
325
+ // Derive next address (chained through all states including current)
326
+ const nextPub = deriveChainedPubkey(hexToBytes(trail.publicKeyBase), allStates);
327
+ const nextXonly = nextPub.slice(1);
328
+ const nextScript = p2trScript(nextXonly);
329
+
330
+ // Fetch current UTXO scriptPubKey
331
+ const txResp = await fetch(`${explorer}/tx/${priv.txid}`);
332
+ if (!txResp.ok) { console.error('Could not fetch current UTXO'); process.exit(1); }
333
+ const txData = await txResp.json();
334
+ const prevOut = txData.vout?.[priv.vout];
335
+ if (!prevOut) { console.error('Current UTXO not found'); process.exit(1); }
336
+
337
+ const fee = 300;
338
+ const outputAmount = priv.amount - fee;
339
+ if (outputAmount <= 546) { console.error('Trail UTXO too small. Fund with: git mark init --voucher ...'); process.exit(1); }
340
+
341
+ const rawTx = buildTransaction(
342
+ { txid: priv.txid, vout: priv.vout, amount: priv.amount, scriptPubKey: hexToBytes(prevOut.scriptpubkey) },
343
+ [{ amount: outputAmount, scriptPubKey: nextScript }],
344
+ signingKey
345
+ );
346
+ const newTxid = await broadcastTx(rawTx, explorer);
347
+
348
+ // Update trail
349
+ trail.states.push(head);
350
+ trail.txo.push(`txo:${chain}:${newTxid}:0?commit=${head}`);
351
+ saveTrail(trail);
352
+
353
+ // Update private state
354
+ savePrivateState({ txid: newTxid, vout: 0, amount: outputAmount });
355
+
356
+ const address = pubkeyToAddress(trail.publicKeyBase, allStates, chain);
357
+ console.log(`Marked: ${head.slice(0, 8)} → ${newTxid.slice(0, 16)}...`);
358
+ console.log(`Address: ${address}`);
359
+ console.log(`Balance: ${outputAmount} sats`);
360
+ console.log(`TXO: txo:${chain}:${newTxid}:0?commit=${head}`);
361
+ }
362
+
363
+ async function cmdInfo() {
364
+ const trail = loadTrail();
365
+ if (!trail) { console.error(`No ${TRAIL_FILE} found.`); process.exit(1); }
366
+ const priv = loadPrivateState();
367
+
368
+ console.log(`Profile: ${trail.profile}`);
369
+ console.log(`Version: ${trail.version}`);
370
+ console.log(`Chain: ${trail.chain}`);
371
+ console.log(`Base public key: ${trail.publicKeyBase}`);
372
+ console.log(`Base address: ${pubkeyToAddress(trail.publicKeyBase, [], trail.chain)}`);
373
+ console.log(`Marks: ${trail.states.length}`);
374
+ if (trail.states.length > 0) {
375
+ const currentAddr = pubkeyToAddress(trail.publicKeyBase, trail.states, trail.chain);
376
+ console.log(`Current address: ${currentAddr}`);
377
+ console.log(`Last commit: ${trail.states[trail.states.length - 1]}`);
378
+ console.log(`Last TXO: ${trail.txo[trail.txo.length - 1]}`);
379
+ }
380
+ if (priv) {
381
+ console.log(`Balance: ${priv.amount} sats`);
382
+ } else {
383
+ console.log('Balance: unfunded');
384
+ }
385
+ }
386
+
387
+ async function cmdVerify() {
388
+ const trail = loadTrail();
389
+ if (!trail) { console.error(`No ${TRAIL_FILE} found.`); process.exit(1); }
390
+ if (trail.states.length === 0) { console.log('No marks to verify.'); return; }
391
+
392
+ const chain = CHAINS[trail.chain];
393
+ if (!chain) { console.error(`Unknown chain: ${trail.chain}`); process.exit(1); }
394
+
395
+ console.log(`Verifying ${trail.states.length} mark(s)...`);
396
+ let ok = true;
397
+
398
+ for (let i = 0; i < trail.states.length; i++) {
399
+ const statesUpTo = trail.states.slice(0, i + 1);
400
+ const expectedAddr = pubkeyToAddress(trail.publicKeyBase, statesUpTo, trail.chain);
401
+ const txoUri = trail.txo[i];
402
+ const parsed = parseTxoUri(txoUri);
403
+
404
+ try {
405
+ const resp = await fetch(`${chain.explorer}/tx/${parsed.txid}`);
406
+ if (!resp.ok) { console.log(` [${i}] FAIL — tx not found: ${parsed.txid.slice(0, 16)}...`); ok = false; continue; }
407
+ const txData = await resp.json();
408
+ const out = txData.vout?.[parsed.vout];
409
+ if (!out) { console.log(` [${i}] FAIL — output ${parsed.vout} not found`); ok = false; continue; }
410
+ if (out.scriptpubkey_address === expectedAddr) {
411
+ console.log(` [${i}] OK — ${trail.states[i].slice(0, 8)} → ${expectedAddr.slice(0, 20)}...`);
412
+ } else {
413
+ console.log(` [${i}] FAIL — address mismatch`);
414
+ console.log(` expected: ${expectedAddr}`);
415
+ console.log(` got: ${out.scriptpubkey_address}`);
416
+ ok = false;
417
+ }
418
+ } catch (e) {
419
+ console.log(` [${i}] ERROR — ${e.message}`);
420
+ ok = false;
421
+ }
422
+ }
423
+
424
+ console.log(ok ? '\nAll marks verified.' : '\nVerification failed.');
425
+ process.exit(ok ? 0 : 1);
426
+ }
427
+
428
+ // --- CLI ---
429
+ const args = process.argv.slice(2);
430
+ const cmd = args[0];
431
+
432
+ if (cmd === 'init') {
433
+ cmdInit(args.slice(1));
434
+ } else if (cmd === 'info') {
435
+ cmdInfo();
436
+ } else if (cmd === 'verify') {
437
+ cmdVerify();
438
+ } else if (cmd === 'mark' || !cmd || (cmd && !cmd.startsWith('-'))) {
439
+ // Default action is mark (git mark = git mark mark)
440
+ // But if no trail exists, suggest init
441
+ if (!existsSync(TRAIL_FILE) && cmd !== 'mark') {
442
+ console.log('Usage:');
443
+ console.log(' git mark init [--chain tbtc4] [--voucher txo:...]');
444
+ console.log(' git mark # anchor HEAD to Bitcoin');
445
+ console.log(' git mark info # show trail state');
446
+ console.log(' git mark verify # verify trail against Bitcoin');
447
+ } else {
448
+ cmdMark(args.slice(cmd === 'mark' ? 1 : 0));
449
+ }
450
+ } else {
451
+ console.log('Usage:');
452
+ console.log(' git mark init [--chain tbtc4] [--voucher txo:...]');
453
+ console.log(' git mark # anchor HEAD to Bitcoin');
454
+ console.log(' git mark info # show trail state');
455
+ console.log(' git mark verify # verify trail against Bitcoin');
456
+ }
package/index.html ADDED
@@ -0,0 +1,141 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>git mark — Anchor Git Commits to Bitcoin</title>
7
+ <meta name="description" content="Mark your git commits on Bitcoin using blocktrails key chaining. Tamper-proof history, globally verifiable, two dependencies.">
8
+ <meta property="og:title" content="git mark — Anchor Git Commits to Bitcoin">
9
+ <meta property="og:description" content="Mark your git commits on Bitcoin using blocktrails key chaining. Tamper-proof history, globally verifiable.">
10
+ <meta property="og:type" content="website">
11
+ <meta property="og:url" content="https://git-mark.com">
12
+ <style>
13
+ * { margin: 0; padding: 0; box-sizing: border-box; }
14
+ body { font-family: Georgia, 'Times New Roman', serif; background: #fafaf8; color: #2c2c2c; line-height: 1.7; padding: 2rem; }
15
+ .container { max-width: 720px; margin: 0 auto; }
16
+ h1 { font-size: 2.2rem; font-weight: 400; margin-bottom: 0.25rem; }
17
+ .subtitle { color: #666; font-size: 1.1rem; margin-bottom: 0.5rem; }
18
+ .meta { color: #999; font-size: 0.85rem; margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #ddd; }
19
+ .meta a { color: #999; }
20
+ h2 { font-size: 1.3rem; color: #f7931a; margin: 2.5rem 0 1rem; font-weight: 600; }
21
+ p { margin-bottom: 1.25rem; }
22
+ code { background: #f0efeb; padding: 0.15rem 0.4rem; border-radius: 3px; font-family: 'SFMono-Regular', Consolas, monospace; font-size: 0.85em; color: #555; }
23
+ pre { background: #f0efeb; padding: 1rem; border-radius: 4px; overflow-x: auto; font-family: 'SFMono-Regular', Consolas, monospace; font-size: 0.85rem; margin: 1.25rem 0; line-height: 1.5; }
24
+ a { color: #1a5276; }
25
+ strong { font-weight: 600; }
26
+ table { width: 100%; border-collapse: collapse; margin: 1.5rem 0; font-size: 0.9rem; }
27
+ th { text-align: left; padding: 0.6rem 0.75rem; border-bottom: 2px solid #ddd; color: #888; font-weight: 500; }
28
+ td { padding: 0.5rem 0.75rem; border-bottom: 1px solid #eee; }
29
+ .highlight { color: #f7931a; font-weight: 600; }
30
+ .callout { background: #fff8f0; border: 1px solid #f7931a33; border-radius: 4px; padding: 1.25rem; margin: 1.5rem 0; }
31
+ .callout .label { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.1em; color: #f7931a; margin-bottom: 0.5rem; }
32
+ footer { margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid #ddd; color: #999; font-size: 0.85rem; }
33
+ footer a { color: #888; }
34
+ </style>
35
+ </head>
36
+ <body>
37
+ <div class="container">
38
+
39
+ <h1>git mark</h1>
40
+ <div class="subtitle">Anchor git commits to Bitcoin via blocktrails</div>
41
+ <div class="meta">
42
+ <a href="https://github.com/solidpayorg/gitmark">GitHub</a> &middot;
43
+ <a href="https://www.npmjs.com/package/gitmark">npm</a> &middot;
44
+ <a href="https://blocktrails.org">blocktrails.org</a>
45
+ </div>
46
+
47
+ <h2>What It Does</h2>
48
+
49
+ <p><span class="highlight">Every <code>git mark</code> creates a Bitcoin transaction</span> that anchors your current commit to the blockchain. The transaction address is derived from your key + the commit hash using BIP-341 key chaining. Anyone can verify the anchor. Nobody can tamper with it.</p>
50
+
51
+ <p>The result is a two-way link: the blockchain follows your repo (via tweaked addresses), and your repo follows the blockchain (via <code>blocktrails.json</code>).</p>
52
+
53
+ <h2>Install</h2>
54
+
55
+ <pre>npm install -g gitmark</pre>
56
+
57
+ <p>This gives you <code>git mark</code> as a native git subcommand.</p>
58
+
59
+ <h2>Quick Start</h2>
60
+
61
+ <pre># Initialize in a git repo
62
+ git mark init --chain tbtc4 --voucher txo:tbtc4:txid:vout?amount=X&amp;key=Y
63
+
64
+ # Make commits, then mark them
65
+ git commit -m "my change"
66
+ git mark
67
+
68
+ # Verify the trail
69
+ git mark verify
70
+
71
+ # Show trail state
72
+ git mark info</pre>
73
+
74
+ <h2>How It Works</h2>
75
+
76
+ <table>
77
+ <tr><th>Step</th><th>What Happens</th></tr>
78
+ <tr><td>Init</td><td>Generates or reads your Nostr key, creates <code>blocktrails.json</code>, optionally funds the trail</td></tr>
79
+ <tr><td>Mark</td><td>Takes HEAD commit hash, derives a tweaked taproot address via BIP-341, builds and broadcasts a Bitcoin transaction</td></tr>
80
+ <tr><td>Verify</td><td>Walks the trail, re-derives each address from the public key + commit hashes, checks each transaction on Bitcoin</td></tr>
81
+ </table>
82
+
83
+ <p>Each mark advances the key chain: <code>newKey = baseKey + tweak(commit_hash)</code>. The chain of derived addresses on Bitcoin mirrors the chain of commits in git.</p>
84
+
85
+ <h2>The Trail File</h2>
86
+
87
+ <p><code>blocktrails.json</code> lives in your repo root, committed and visible:</p>
88
+
89
+ <pre>{
90
+ "version": "0.0.3",
91
+ "profile": "gitmark",
92
+ "publicKeyBase": "02abc...",
93
+ "chain": "tbtc4",
94
+ "states": ["a1b2c3...", "e5f6a7..."],
95
+ "txo": [
96
+ "txo:tbtc4:abc:0?commit=a1b2c3...",
97
+ "txo:tbtc4:def:0?commit=e5f6a7..."
98
+ ]
99
+ }</pre>
100
+
101
+ <p><strong>states</strong> — the commit hashes (input to key chaining math). <strong>txo</strong> — the Bitcoin anchors (TXO URIs, self-contained and verifiable). Anyone who clones your repo can verify the trail independently.</p>
102
+
103
+ <h2>Key Storage</h2>
104
+
105
+ <p>Your private key is stored in git config:</p>
106
+
107
+ <pre>git config nostr.privkey &lt;64-char-hex&gt;</pre>
108
+
109
+ <p>The same secp256k1 key you use for Nostr, Bitcoin, and Solid pod authentication. One key for everything.</p>
110
+
111
+ <div class="callout">
112
+ <div class="label">Blocktrails</div>
113
+ <p style="margin-bottom: 0.75rem">Git-mark is a <a href="https://blocktrails.org">blocktrails</a> application profile. The key chaining math is identical to <a href="https://blocktrails.org">MRC20 tokens</a> — just with commit hashes as state instead of token ledgers. A generic blocktrails verifier handles both.</p>
114
+ <p style="margin-bottom: 0">Two dependencies: <code>@noble/curves</code> and <code>@noble/hashes</code>. No bitcoinjs-lib. No external transaction builders. Pure secp256k1 math.</p>
115
+ </div>
116
+
117
+ <h2>Commands</h2>
118
+
119
+ <table>
120
+ <tr><th>Command</th><th>Description</th></tr>
121
+ <tr><td><code>git mark init</code></td><td>Initialize trail. Options: <code>--chain</code>, <code>--voucher</code>, <code>--force</code></td></tr>
122
+ <tr><td><code>git mark</code></td><td>Anchor HEAD commit to Bitcoin</td></tr>
123
+ <tr><td><code>git mark info</code></td><td>Show trail state, balance, addresses</td></tr>
124
+ <tr><td><code>git mark verify</code></td><td>Verify all marks against Bitcoin</td></tr>
125
+ </table>
126
+
127
+ <h2>Related</h2>
128
+
129
+ <p>
130
+ <a href="https://blocktrails.org">Blocktrails</a> &mdash; state anchoring on Bitcoin<br>
131
+ <a href="https://github.com/nicepkg/gitmark-test">gitmark-test</a> &mdash; interim implementation<br>
132
+ <a href="https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki">BIP-341</a> &mdash; Taproot key tweaking
133
+ </p>
134
+
135
+ <footer>
136
+ <p>&copy; 2026 <a href="https://melvin.me">Melvin Carvalho</a> &middot; MIT License &middot; <a href="https://github.com/solidpayorg/gitmark">Source</a></p>
137
+ </footer>
138
+
139
+ </div>
140
+ </body>
141
+ </html>
package/package.json CHANGED
@@ -1,40 +1,34 @@
1
1
  {
2
2
  "name": "gitmark",
3
- "version": "0.0.68",
4
- "description": "gitmark",
5
- "main": "index.js",
3
+ "version": "0.0.70",
4
+ "type": "module",
5
+ "description": "Anchor git commits to Bitcoin via blocktrails",
6
+ "main": "bin/git-mark.js",
7
+ "bin": {
8
+ "git-mark": "bin/git-mark.js"
9
+ },
6
10
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
11
+ "test": "node bin/git-mark.js --help"
8
12
  },
9
13
  "repository": {
10
14
  "type": "git",
11
15
  "url": "git+https://github.com/solidpayorg/gitmark.git"
12
16
  },
13
17
  "keywords": [
14
- "gitmark"
18
+ "gitmark",
19
+ "blocktrails",
20
+ "bitcoin",
21
+ "git",
22
+ "nostr"
15
23
  ],
16
- "bin": {
17
- "git-mark": "bin/git-mark",
18
- "git-mark-init": "bin/git-mark-init",
19
- "git-mark-list": "bin/git-mark-list",
20
- "git-mark-verify": "bin/git-mark-verify"
21
- },
22
24
  "author": "Melvin Carvalho",
23
25
  "license": "MIT",
24
26
  "bugs": {
25
27
  "url": "https://github.com/solidpayorg/gitmark/issues"
26
28
  },
27
- "homepage": "https://github.com/solidpayorg/gitmark#readme",
29
+ "homepage": "https://git-mark.com",
28
30
  "dependencies": {
29
- "bip-schnorr": "^0.6.4",
30
- "bitcoinjs-lib": "^5.2.0",
31
- "child_process": "^1.0.2",
32
- "fs": "^0.0.1-security",
33
- "gitlog": "^4.0.4",
34
- "gitmark": "^0.0.56",
35
- "minimist": "^1.2.5",
36
- "sha256": "^0.2.0",
37
- "tiny-secp256k1": "^1.1.6",
38
- "ws": "^8.2.3"
31
+ "@noble/curves": "^1.2.0",
32
+ "@noble/hashes": "^1.3.0"
39
33
  }
40
- }
34
+ }