AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Kotlin RDF stack: RDF 1.2, SHACL 1.2, graph & SPARQL DSLs, Kastor Gen (KSP), ontology-quality tooling; backends: Jena, RDF4J, memory, SPARQL.
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
More marks: logo variants (dark background, icon, wordmark).
Documentation website — Jekyll site in docs/, deployed with GitHub Actions. On a fork, enable Settings → Pages → Build and deployment: GitHub Actions.
Reading the docs: How documentation fits together · Glossary.
Contributors: repository architecture (Gradle modules, layers, and on-disk layout) and CONTRIBUTING (build, tests, CI).查看完整 README ↗Documentation website — Jekyll site in docs/, deployed with GitHub Actions. On a fork, enable Settings → Pages → Build and deployment: GitHub Actions.
Reading the docs: How documentation fits together · Glossary.
Contributors: repository architecture (Gradle modules, layers, and on-disk layout) and CONTRIBUTING (build, tests, CI).
Kastor is a modern, comprehensive Kotlin framework for RDF (Resource Description Framework) and semantic web development. It bridges the gap between traditional object-oriented programming and semantic technologies, making RDF accessible and powerful for Kotlin developers.
💝 Support Kastor: If this project helps you, consider sponsoring to ensure continued maintenance and feature development. Organizations using Kastor in production can contact us for enterprise support and custom adaptations.
See release contracts and verification for tested capabilities, resource ownership, generated snapshots, and experimental limitations.
Use Kastor when you want domain-first RDF in Kotlin: pure domain interfaces with a side-channel back to RDF, plus a vocabulary-agnostic DSL that keeps RDF explicit without forcing RDF types into your business code.
Philosophy
Anti-goals
Comparison (neutral)
| Concern | Kastor | Jena / RDF4J / rdflib |
|---|---|---|
| Domain objects without RDF dependencies | ✅ | ❌ |
| Side-channel RDF access | ✅ | ❌ (typically direct RDF types) |
| Vocabulary-agnostic DSL | ✅ | ⚠️ (varies) |
| Provider-agnostic core API | ✅ | ❌ (engine-specific APIs) |
Kastor doesn't replace Jena or RDF4J—it makes them easier to use in Kotlin.
✅ Keep your existing infrastructure: Use your current repositories, stores, and configurations
✅ No migration required: Your data and queries work as-is
✅ Gradual adoption: Use Kastor for new code, keep existing code unchanged
✅ Full access: Reach underlying Jena/RDF4J APIs when needed
✅ Same backends: TDB2, NativeStore, memory stores—all supported
RDF-H 0.8: the optional rdf-holon module resolves an explicit holon registry over named graphs,
RDF 1.2 reifiers, or declared mixed data. It supports direct, aggregated and visible content,
attribution, checked conversion, and scoped SHACL validation. See RDF-H usage.
🎨 Natural language DSL: person has FOAF.name with "Alice" instead of verbose Model API calls
🔒 Type-safe SPARQL: Compile-time validated queries with Kotlin-idiomatic builders
🚀 Kotlin idioms: Extension functions, sealed classes, null safety, coroutines
🔄 Provider-agnostic: Switch between Jena, RDF4J, Memory, or SPARQL endpoints without changing your code
import com.geoknoesis.kastor.rdf.*
import com.geoknoesis.kastor.rdf.jena.*
import com.geoknoesis.kastor.rdf.vocab.FOAF
import org.apache.jena.rdf.model.Model
import org.apache.jena.rdf.model.ModelFactory
// Your existing Jena Model
val jenaModel: Model = ModelFactory.createDefaultModel()
// Wrap it with Kastor for easier Kotlin development.
// Reads are strict: a statement Kastor cannot represent (e.g. xml:lang="en_US") fails the read.
// Use JenaBridge.fromJenaModel(jenaModel, strictRead = false) to skip such statements instead.
val graph = jenaModel.toKastorGraph()
// Now use Kastor's DSL
graph.add {
val person = iri("http://example.org/alice")
person has FOAF.name with "Alice"
person has FOAF.age with 30
}
// Or access Jena directly when needed
val underlyingModel = graph.toJenaModel()
val statement = underlyingModel.listStatements().next()
import com.geoknoesis.kastor.rdf.*
import com.geoknoesis.kastor.rdf.vocab.FOAF
import org.eclipse.rdf4j.repository.Repository
// Your existing RDF4J repository
val rdf4jRepo: Repository = // ... your existing setup
// Wrap the initialized repository. Closing the adapter also shuts down rdf4jRepo.
// Graph reads are strict; pass Rdf4jRepository(rdf4jRepo, inference = false, lenientRead = true)
// to skip statements Kastor cannot represent instead of failing.
val repo = com.geoknoesis.kastor.rdf.rdf4j.Rdf4jRepository(rdf4jRepo)
// Now write cleaner Kotlin code
repo.add {
val person = iri("http://example.org/alice")
person has FOAF.name with "Alice"
}
// Same API works with Jena, Memory, or SPARQL endpoints
val jenaRepo = Rdf.repository {
providerId = "jena"
variantId = "tdb2"
location = "/data/tdb2"
}
// Same code, different backend!
Bottom line: Kastor is a compatibility layer that makes RDF programming easier in Kotlin. You keep your existing infrastructure and get a better developer experience.
Use the kastor-bom when you want every published module pinned to one version. When trimming the classpath:
implementation("com.geoknoesis.kastor:rdf-core") plus rdf-jena or rdf-rdf4j (Maven artifact IDs).RdfReasoning, ReasonerRegistry): add rdf-reasoning and the adapter-specific rdf-jena-reasoning and/or rdf-rdf4j-reasoning artifacts (they are no longer pulled in transitively by the store adapters).See Repository architecture — Dependency profiles for the full table.
Kastor provides a dual-layer architecture that serves both RDF experts and application developers:
rdf-testkit, rdf-cli; see Testing RDF graphs)rdf:reifies and triple terms (Jena and RDF4J providers)person `is` FOAF.Person)The name Kastor is a tribute to Castor, the pioneering Java XML data binding framework developed by Exolab. Castor was one of the first frameworks to bridge the gap between object-oriented programming and structured data formats, making it easier for developers to work with XML data in Java applications.
Just as Castor revolutionized XML data binding in Java, Kastor aims to bring the same level of developer experience to RDF and semantic web technologies in Kotlin. The "K" in Kastor represents:
Kastor honors Castor's legacy while embracing the future of semantic technologies and the Kotlin ecosystem.
Core Standards
rdf:reifies, rdf:dirLangString)<<( s p o )>> syntax, LANGDIR / STRLANGDIR, TRIPLE family)Validation & Semantics
rdf-jena or rdf-rdf4j (see SHACL validation)Serialization Formats
Other
rdf:reifies (RDF 1.2 native; provider-dependent)The bundled in-memory provider is RDF 1.1 only. RDF 1.2 features (triple terms, directional language strings, the new format syntaxes) require
rdf-jenaorrdf-rdf4jon the classpath. See docs/kastor/concepts/rdf-1.2.md and the migration guide for upgrading from 0.1.x.
Conformance: Kastor includes a W3C RDF 1.2 syntax harness in :rdf:conformance (Turtle 1.2, TriG 1.2, N-Triples 1.2, N-Quads 1.2) for Jena and RDF4J. Use ./gradlew conformanceSmokeTest for a fast bundled check; run python scripts/fetch-conformance-data.py (pinned W3C corpora) and then ./gradlew :rdf:conformance:test for the full upstream corpus. Details:
docs/kastor/concepts/rdf-1.2-conformance.md.
rdf-testkit — RDF-isomorphic assertions and golden Turtle checks; rdf-cli — parse, to-turtle, and diff from the command line (guide)person `is` FOAF.Person, person has FOAF.name with "Alice")reifies(...) DSL for statement metadata (replaces the RDF-star model of 0.1.x)qname("foaf:name") resolves declared and built-in prefixes to IRIsKey Benefits:
Rdf.repository { }withSelectRows, withConstructTriples and Rdf.openTripleStream release backend resources deterministically (release contract)Required runtime dependency:
Rdf.memory()andRdf.persistent()need a SPARQL-capable provider on the classpath. Add eithercom.geoknoesis.kastor:rdf-jenaorcom.geoknoesis.kastor:rdf-rdf4jto your dependencies. Without one, both factories throwRdfProviderException. The bundled:rdf:core-only memory provider is graph-only and must be opted into explicitly viaRdf.repository { providerId = "memory" }.
import com.geoknoesis.kastor.rdf.*
import com.geoknoesis.kastor.rdf.vocab.FOAF
import com.geoknoesis.kastor.rdf.vocab.RDF
// Create a repository (needs rdf-jena or rdf-rdf4j on the classpath)
val repo = Rdf.memory()
repo.add {
prefixes {
put("ex", "http://example.org/")
}
val alice = qname("ex:alice") // QName → <http://example.org/alice>
val bob = iri("http://example.org/bob")
alice - RDF.type - FOAF.Person // minus-operator form
bob `is` FOAF.Person // natural-language alias for rdf:type
alice - FOAF.name - "Alice" // strings become xsd:string literals
alice has FOAF.age with 30 // has/with form; Int becomes xsd:integer
alice[FOAF.knows] = bob // bracket form
}
// Query with a SPARQL string
val results = repo.select(SparqlSelectQuery("""
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?name WHERE { ?person a foaf:Person ; foaf:name ?name }
"""))
results.forEach { row -> println(row.get("name")) }
Requires kastor-gen-runtime and the KSP processor (kastor-gen-processor) to generate the implementation.
import com.geoknoesis.kastor.gen.annotations.Rdf // the annotation (not the Rdf factory object)
import com.geoknoesis.kastor.gen.runtime.materialize
import com.geoknoesis.kastor.gen.runtime.materializeIn
import com.geoknoesis.kastor.rdf.iri
// Define domain interface
@Rdf(iri = "http://xmlns.com/foaf/0.1/Person")
interface Person {
@Rdf(iri = "http://xmlns.com/foaf/0.1/name")
val name: String
@Rdf(iri = "http://xmlns.com/foaf/0.1/age")
val age: Int
}
// Materialize from RDF (idiomatic — reified domain type)
val person: Person = repo.materialize(iri("http://example.org/person"))
// Equivalently: repo.defaultGraph.materialize(iri("http://example.org/person"))
// or: iri("http://example.org/person").materializeIn(repo.defaultGraph)
// Use domain object
println("Name: ${person.name}, Age: ${person.age}")
String objects are always literals, and IRIs are explicit. Use qname(...) to expand a prefixed name with declared or built-in prefixes, and iri(...) for full IRIs:
import com.geoknoesis.kastor.rdf.*
import com.geoknoesis.kastor.rdf.vocab.RDF
repo.add {
// Built-in prefixes need no declaration: rdf, rdfs, owl, sh, xsd, obo,
// skos, prov, dcat, dcterms, void, geo, time
prefixes {
put("foaf", "http://xmlns.com/foaf/0.1/") // declare (or override) prefixes
}
prefix("ex", "http://example.org/") // single-prefix form
val person = qname("ex:person")
person - RDF.type - qname("foaf:Person") // → <http://xmlns.com/foaf/0.1/Person>
person - qname("rdfs:label") - "Person" // predicate from a built-in prefix
person - qname("foaf:homepage") - iri("http://example.org/profile")
}
import com.geoknoesis.kastor.rdf.* // Direction, add, iri, lit, string
import com.geoknoesis.kastor.rdf.vocab.FOAF
import com.geoknoesis.kastor.rdf.vocab.XSD
repo.add {
val person = iri("http://example.org/person")
person - FOAF.name - string("foaf:Person") // "foaf:Person"^^xsd:string (never an IRI)
person - FOAF.name - lang("Alice", "en") // "Alice"@en
person - FOAF.name - lang("مرحبا", "ar", Direction.RTL) // RDF 1.2 directional string
person - FOAF.age - lit("30", XSD.integer) // "30"^^xsd:integer
}
The Kotlin samples in this README are compiled by the build; see examples/hello-world/src/main/kotlin/ReadmeSnippets.kt.
Not yet on Maven Central. Kastor artifacts have not been published yet. Build from source and install into your local Maven repository with
./gradlew publishToMavenLocal, then addmavenLocal()to your repositories. You can also include the Kastor checkout as a composite build (includeBuild("../kastor")). The version (0.3.0-SNAPSHOT) is defined once ingradle.properties. Requires JDK 21+ and Kotlin 2.4.
dependencies {
// Align all Kastor modules
implementation(platform("com.geoknoesis.kastor:kastor-bom:0.3.0-SNAPSHOT"))
// Kastor RDF Core (transitive: rdf-sparql-contract for query markers)
implementation("com.geoknoesis.kastor:rdf-core")
// Backends (at least one for SPARQL)
implementation("com.geoknoesis.kastor:rdf-jena")
implementation("com.geoknoesis.kastor:rdf-rdf4j")
// Kastor Gen
implementation("com.geoknoesis.kastor:kastor-gen-runtime")
ksp("com.geoknoesis.kastor:kastor-gen-processor:0.3.0-SNAPSHOT")
// Optional: Kotlin SPARQL query DSL / sparql package / flows
implementation("com.geoknoesis.kastor:rdf-sparql-lang")
// Optional: SHACL shapes DSL (shacl {}, Rdf.shacl); pulls rdf-sparql-lang transitively
implementation("com.geoknoesis.kastor:rdf-shacl-dsl")
// Optional: SHACL validation, reasoning
implementation("com.geoknoesis.kastor:rdf-shacl-validation")
implementation("com.geoknoesis.kastor:rdf-reasoning")
}
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.geoknoesis.kastor</groupId>
<artifactId>kastor-bom</artifactId>
<version>0.3.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.geoknoesis.kastor</groupId>
<artifactId>rdf-core</artifactId>
</dependency>
<!-- Kotlin SPARQL query DSL (optional) -->
<dependency>
<groupId>com.geoknoesis.kastor</groupId>
<artifactId>rdf-sparql-lang</artifactId>
</dependency>
<!-- Kotlin SHACL shapes DSL (optional; transitive rdf-sparql-lang) -->
<dependency>
<groupId>com.geoknoesis.kastor</groupId>
<artifactId>rdf-shacl-dsl</artifactId>
</dependency>
<dependency>
<groupId>com.geoknoesis.kastor</groupId>
<artifactId>kastor-gen-runtime</artifactId>
</dependency>
</dependencies>
Kastor is designed for (though not yet proven in) domains such as:
Kastor's architecture is designed around separation of concerns and pluggable components:
┌─────────────────────────────────────────────────────────────┐
│ Kastor Framework │
├─────────────────────────────────────────────────────────────┤
│ 🎨 Natural Language DSL │ 🛡️ Type Safety │ ⚡ Performance │
├─────────────────────────────────────────────────────────────┤
│ Application Layer │
│ 📊 Domain Objects │ 🔄 Materialization │ 🎯 Code Gen │
├─────────────────────────────────────────────────────────────┤
│ Core API Layer │
│ 📈 Graph Ops │ 🔍 SPARQL │ 🧠 Reasoning │ ✅ Validation │
├─────────────────────────────────────────────────────────────┤
│ Provider Layer (Pluggable) │
│ 💾 Memory │ 🏢 Jena │ 🔧 RDF4J │ 🌐 SPARQL │ 🔌 Custom │
└─────────────────────────────────────────────────────────────┘
Layer 1: RDF Core - For semantic web experts
Layer 2: Kastor Gen - For application developers
git clone https://github.com/geoknoesis/kastor.git
cd kastor
./gradlew assemble
./gradlew publishToMavenLocal # optional: consume 0.3.0-SNAPSHOT from mavenLocal()
./gradlew check -x :rdf:conformance:test
./gradlew conformanceSmokeTest
Requires JDK 21+ and Python 3: check also runs the repository quality gates (verifyQualityGates: complexity ratchet, documentation versions, build pins, README imports). See CONTRIBUTING for the full W3C conformance suites, memory settings, and the release process.
Kastor is developed by GeoKnoesis LLC, a company specializing in semantic web technologies and knowledge engineering.
Kastor is open-source and free to use, but maintaining and evolving it requires ongoing effort.
If Kastor is valuable to you or your organization, your financial support helps ensure:
Ways to support:
For organizations: If you're using Kastor in production or need specific features, consider enterprise sponsorship. This helps prioritize your needs and ensures the project continues to evolve in ways that benefit your use case.
See CHANGELOG.md for the full list of changes between releases.
Kastor is licensed under the Apache License 2.0.
Ready to get started? Check out our Getting Started Guide or explore the examples!