Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions java/PUBLISHING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Java Publishing (Maven Central)

This doc describes how to publish the Java modules in `java/` to Maven Central under the `com.selfid` groupId using the **Sonatype Central Portal** flow.

## Modules

- `cid`
- `crypto`
- `gatekeeper`
- `keymaster`

## Prerequisites

1. Sonatype account with the **com.selfid** namespace verified (Central Portal).
2. A GPG signing key (ASCII-armored) for signing artifacts.
3. Access to **Central Portal**: https://central.sonatype.com/

## Configuration

Publishing is configured in `java/build.gradle` and `java/gradle.properties`.

- Group + version are set in `java/gradle.properties`.
- Example: `group=com.selfid`, `version=1.0.0`
- POM metadata is defined in `java/build.gradle` (name, description, SCM, licenses, developers).

## Secrets (Environment Variables)

Set these before publishing:

- `SONATYPE_USERNAME` (Central Portal **User Token** username)
- `SONATYPE_PASSWORD` (Central Portal **User Token** password)
- `SIGNING_KEY` (ASCII-armored private key text)
- `SIGNING_PASSWORD`

Optional:

- `CENTRAL_PUBLISHING_TYPE` (default: `USER_MANAGED`)
- `CENTRAL_DEPLOYMENT_NAME` (default: `Self ID Java <version>`)

Example:

```bash
export SONATYPE_USERNAME=your_token_username
export SONATYPE_PASSWORD=your_token_password
export SIGNING_KEY="$(cat /path/to/privatekey.asc)"
export SIGNING_PASSWORD=your_key_password
```

## Build and Publish (Central Portal)

From `java/`:

```bash
./gradlew clean
./gradlew publishCentral
```

This will:

1. Publish all subprojects into a local **central bundle** directory
2. Generate required checksum files
3. Zip the bundle
4. Upload it to Central Portal

### Publish a Single Module (bundle only)

```bash
./gradlew :cid:publishMavenJavaPublicationToCentralBundleRepository
```

To upload the full bundle after that, still run:

```bash
./gradlew uploadCentralBundle
```

## Release in Central Portal

After upload, log into Central Portal and follow the deployment status. If `CENTRAL_PUBLISHING_TYPE` is `USER_MANAGED`, you will need to manually release the deployment there.

22 changes: 11 additions & 11 deletions java/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ Modules:
- `keymaster` — wallet + credential operations.

Compatibility and runtime
- JDK 17 is required (targeted bytecode and test runtime).
- JDK 11 is required (targeted bytecode and test runtime).
- Dependencies are pure-Java artifacts (OkHttp, Jackson, Bouncy Castle, Tink, bitcoinj).
- Gatekeeper default base URL is `http://localhost:4224` (see `GatekeeperClientOptions`).

Quickstart (Gradle)
```gradle
dependencies {
implementation("org.keychain:cid:<version>")
implementation("org.keychain:crypto:<version>")
implementation("org.keychain:gatekeeper:<version>")
implementation("org.keychain:keymaster:<version>")
implementation("com.selfid:cid:1.0.0")
implementation("com.selfid:crypto:1.0.0")
implementation("com.selfid:gatekeeper:1.0.0")
implementation("com.selfid:keymaster:1.0.0")
}
```

Expand All @@ -30,12 +30,12 @@ import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.keychain.gatekeeper.GatekeeperClient;
import org.keychain.gatekeeper.GatekeeperClientOptions;
import org.keychain.keymaster.Keymaster;
import org.keychain.keymaster.model.WalletEncFile;
import org.keychain.keymaster.store.WalletJson;
import org.keychain.keymaster.store.WalletStore;
import com.selfid.gatekeeper.GatekeeperClient;
import com.selfid.gatekeeper.GatekeeperClientOptions;
import com.selfid.keymaster.Keymaster;
import com.selfid.keymaster.model.WalletEncFile;
import com.selfid.keymaster.store.WalletJson;
import com.selfid.keymaster.store.WalletStore;

Path dataDir = Paths.get(System.getProperty("user.home"), ".keymaster");
WalletStore<WalletEncFile> store = new WalletJson<>(WalletEncFile.class, dataDir, "wallet.json");
Expand Down
136 changes: 135 additions & 1 deletion java/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
allprojects {
repositories {
mavenCentral()
maven { url = 'https://jitpack.io' }
}
}

Expand Down Expand Up @@ -29,3 +28,138 @@ subprojects {
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.2'
}
}

def publishableModules = ['cid', 'crypto', 'gatekeeper', 'keymaster']
def centralBundleDir = rootProject.layout.buildDirectory.dir("central-bundle")

configure(subprojects.findAll { it.name in publishableModules }) {
apply plugin: 'maven-publish'
apply plugin: 'signing'

java {
withSourcesJar()
withJavadocJar()
}

publishing {
publications {
mavenJava(MavenPublication) {
from components.java
artifactId = project.name

pom {
name = "Self ID Java ${project.name}"
description = "Self ID Java ${project.name} module"
url = "https://selfid.com"

licenses {
license {
name = "MIT License"
url = "https://opensource.org/licenses/MIT"
distribution = "repo"
}
}

developers {
developer {
id = "selfid"
name = "Self ID"
organization = "Self ID"
organizationUrl = "https://selfid.com"
}
}

scm {
url = "https://github.com/KeychainMDIP/kc"
connection = "scm:git:https://github.com/KeychainMDIP/kc.git"
developerConnection = "scm:git:https://github.com/KeychainMDIP/kc.git"
}
}
}
}

repositories {
maven {
name = "CentralBundle"
url = centralBundleDir.get().asFile.toURI()
}
}
}

signing {
def signingKey = findProperty("signingKey") ?: System.getenv("SIGNING_KEY")
def signingPassword = findProperty("signingPassword") ?: System.getenv("SIGNING_PASSWORD")

if (signingKey && signingPassword) {
useInMemoryPgpKeys(signingKey, signingPassword)
}

sign publishing.publications.mavenJava
}
}

tasks.register("cleanCentralBundle", Delete) {
delete centralBundleDir
}

tasks.register("publishCentralBundle") {
dependsOn "cleanCentralBundle"
dependsOn publishableModules.collect { ":${it}:publishMavenJavaPublicationToCentralBundleRepository" }
}

tasks.register("zipCentralBundle", Zip) {
dependsOn "publishCentralBundle"
from centralBundleDir
archiveFileName = "central-bundle.zip"
destinationDirectory = rootProject.layout.buildDirectory.dir("central")
}

tasks.register("uploadCentralBundle") {
dependsOn "zipCentralBundle"
doLast {
def zipFile = tasks.named("zipCentralBundle", Zip).get().archiveFile.get().asFile
if (!zipFile.exists()) {
throw new GradleException("Central bundle zip not found: ${zipFile}")
}

def username = System.getenv("SONATYPE_USERNAME")
def password = System.getenv("SONATYPE_PASSWORD")
if (!username || !password) {
throw new GradleException("SONATYPE_USERNAME and SONATYPE_PASSWORD must be set for Central upload.")
}

def token = "${username}:${password}".bytes.encodeBase64().toString()

def publishingType = System.getenv("CENTRAL_PUBLISHING_TYPE") ?: "USER_MANAGED"
def deploymentName = System.getenv("CENTRAL_DEPLOYMENT_NAME") ?: "Self ID Java ${project.version}"
def url = "https://central.sonatype.com/api/v1/publisher/upload?publishingType=${publishingType}&name=${java.net.URLEncoder.encode(deploymentName, 'UTF-8')}"

def stdout = new ByteArrayOutputStream()
def stderr = new ByteArrayOutputStream()

def result = project.exec {
commandLine "curl",
"--fail",
"--silent",
"--show-error",
"--request", "POST",
"--header", "Authorization: Bearer ${token}",
"--form", "bundle=@${zipFile.absolutePath}",
url
standardOutput = stdout
errorOutput = stderr
ignoreExitValue = true
}

if (result.exitValue != 0) {
throw new GradleException("Central upload failed: ${stderr.toString('UTF-8').trim()}")
}

def deploymentId = stdout.toString("UTF-8").trim()
println("Central upload complete. Deployment ID: ${deploymentId}")
}
}

tasks.register("publishCentral") {
dependsOn "uploadCentralBundle"
}
2 changes: 1 addition & 1 deletion java/cid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Minimal CID validator used by Keymaster for DID checks.
## Usage

```java
import org.keychain.cid.Cid;
import com.selfid.cid.Cid;

boolean ok = Cid.isValid("bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi");
```
Expand Down
8 changes: 4 additions & 4 deletions java/crypto/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ Core crypto utilities that mirror the JS Keymaster behavior.
## Usage

```java
import org.keychain.crypto.HdKey;
import org.keychain.crypto.JwkPair;
import org.keychain.crypto.KeymasterCrypto;
import org.keychain.crypto.KeymasterCryptoImpl;
import com.selfid.crypto.HdKey;
import com.selfid.crypto.JwkPair;
import com.selfid.crypto.KeymasterCrypto;
import com.selfid.crypto.KeymasterCryptoImpl;

KeymasterCrypto crypto = new KeymasterCryptoImpl();

Expand Down
2 changes: 1 addition & 1 deletion java/crypto/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ dependencies {
api 'org.bouncycastle:bcprov-jdk15to18:1.78.1'
api 'com.google.crypto.tink:tink:1.12.0'
api 'org.bitcoinj:bitcoinj-core:0.16.3'
api 'com.github.erdtman:java-json-canonicalization:1.1'
api 'io.github.erdtman:java-json-canonicalization:1.1'
api 'com.fasterxml.jackson.core:jackson-databind:2.17.1'
}
6 changes: 3 additions & 3 deletions java/gatekeeper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ HTTP client for the Gatekeeper REST API.
## Usage

```java
import org.keychain.gatekeeper.GatekeeperClient;
import org.keychain.gatekeeper.GatekeeperClientOptions;
import org.keychain.gatekeeper.model.MdipDocument;
import com.selfid.gatekeeper.GatekeeperClient;
import com.selfid.gatekeeper.GatekeeperClientOptions;
import com.selfid.gatekeeper.model.MdipDocument;

GatekeeperClientOptions options = new GatekeeperClientOptions();
options.baseUrl = "http://localhost:4224";
Expand Down
2 changes: 2 additions & 0 deletions java/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
org.gradle.java.home=/usr/lib/jvm/java-11-openjdk-amd64
group=com.selfid
version=1.0.1
12 changes: 6 additions & 6 deletions java/keymaster/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ Keymaster manages an encrypted wallet and signs Gatekeeper operations.
```java
import java.nio.file.Path;
import java.nio.file.Paths;
import org.keychain.gatekeeper.GatekeeperClient;
import org.keychain.gatekeeper.GatekeeperClientOptions;
import org.keychain.keymaster.Keymaster;
import org.keychain.keymaster.model.WalletEncFile;
import org.keychain.keymaster.store.WalletJson;
import org.keychain.keymaster.store.WalletStore;
import com.selfid.gatekeeper.GatekeeperClient;
import com.selfid.gatekeeper.GatekeeperClientOptions;
import com.selfid.keymaster.Keymaster;
import com.selfid.keymaster.model.WalletEncFile;
import com.selfid.keymaster.store.WalletJson;
import com.selfid.keymaster.store.WalletStore;

Path dataDir = Paths.get(System.getProperty("user.home"), ".keymaster");
WalletStore<WalletEncFile> store = new WalletJson<>(WalletEncFile.class, dataDir, "wallet.json");
Expand Down
Loading