Skip to content

Protobuf sources

Below you will find the Protocol Buffers definitions for the Jelly-RDF, Jelly-Patch, and Jelly-SPARQL formats, along with the Jelly gRPC streaming protocol. The original files are hosted on GitHub and all releases can be found here.

Human-readable reference for these definitions can be found here.

The following code is licensed under the Apache License, Version 2.0.

rdf.proto

syntax = "proto3";
package eu.ostrzyciel.jelly.core.proto.v1;

// Jelly RDF serialization with Protocol Buffers.
// Specification document:
// https://w3id.org/jelly/1.2.0/specification/serialization
// Protocol version: 1.2.0
//
// A Jelly-RDF stream is a sequence of RdfStreamFrame messages. The contents
// of the frames use one of two layouts, depending on the protocol version:
//
//   - Column layout (version 3, Jelly-RDF 1.2.x): the statements of a frame
//     are stored column by column in an RdfColumnBatch.
//   - Row layout (versions 1 and 2, Jelly-RDF 1.0.x and 1.1.x): a frame is a
//     list of RdfStreamRow messages. The messages used only by this layout
//     are in the "Row layout" section at the end of this file.
//
// Comments starting with "If row layout:" or "If column layout:" apply only
// to streams in that layout.

// ---------------------------------------------------------------------------
// RDF terms
// ---------------------------------------------------------------------------

// RDF IRI, encoded as a prefix and a name from the lookup tables. The IRI is
// the prefix followed by the name.
message RdfIri {
  // 1-based identifier of the prefix in the prefix lookup.
  // 0 means "the same prefix as in the previous IRI", or no prefix in the
  // first IRI. Which IRI is the previous one depends on the layout.
  uint32 prefix_id = 1;

  // 1-based identifier of the name in the name lookup.
  // 0 means "the name_id of the previous IRI + 1", or 1 in the first IRI.
  uint32 name_id = 2;
}

// Version of RDF that a stream conforms to, following the version labels of
// RDF 1.2 Concepts:
// https://www.w3.org/TR/rdf12-concepts/#defined-version-labels
enum RdfVersion {
  // Unspecified. Each format that uses this enum defines how to interpret it.
  RDF_VERSION_UNSPECIFIED = 0;
  // RDF 1.1 (version label "1.1"): no triple terms, no base directions.
  RDF_VERSION_1_1 = 1;
  // RDF 1.2 Basic (version label "1.2-basic"): base directions are allowed,
  // triple terms are not.
  RDF_VERSION_1_2_BASIC = 2;
  // RDF 1.2 (version label "1.2"): base directions and triple terms are
  // allowed.
  RDF_VERSION_1_2 = 3;
}

// Initial text direction of a language-tagged string (RDF 1.2).
enum RdfBaseDirection {
  // No base direction: an ordinary language-tagged string (rdf:langString).
  RDF_BASE_DIRECTION_UNSPECIFIED = 0;
  // Left-to-right ("ltr"): a directional language-tagged string
  // (rdf:dirLangString).
  RDF_BASE_DIRECTION_LTR = 1;
  // Right-to-left ("rtl"): a directional language-tagged string
  // (rdf:dirLangString).
  RDF_BASE_DIRECTION_RTL = 2;
}

// RDF literal
message RdfLiteral {
  // The lexical form of the literal.
  string lex = 1;

  // Literal kind – at most one of these fields may be set.
  // If none is set, the literal is a simple literal (xsd:string).
  oneof literalKind {
    // Language tag of a language-tagged string.
    string langtag = 2;
    // 1-based identifier of the datatype in the datatype lookup.
    // 0 is invalid (in contrast to prefix_id and name_id in RdfIri).
    // If column layout: must not refer to rdf:langString or
    // rdf:dirLangString.
    uint32 datatype = 3;
  }

  // Base direction of a language-tagged string (RDF 1.2).
  // If column layout: may be set only if langtag is set.
  // If row layout: must not be set.
  RdfBaseDirection direction = 4;
}

// RDF 1.2 triple term. All three positions must be set, and only the object
// may be a nested triple term. The field numbers are the same as in
// RdfTriple.
//
// Used by the column layout and by Jelly-SPARQL.
message RdfTripleTerm {
  // Subject
  oneof subject {
    // IRI
    RdfIri s_iri = 1;
    // Blank node
    string s_bnode = 2;
  }

  // Predicate
  RdfIri p_iri = 5;

  // Object
  oneof object {
    // IRI
    RdfIri o_iri = 9;
    // Blank node
    string o_bnode = 10;
    // Literal
    RdfLiteral o_literal = 11;
    // Nested triple term
    RdfTripleTerm o_triple_term = 12;
  }
}

// ---------------------------------------------------------------------------
// Lookup tables
// ---------------------------------------------------------------------------

// Packed entries of a lookup table (name, prefix, or datatype): the values of
// a run of consecutive identifiers.
//
// Used by the column layout and by Jelly-SPARQL.
message RdfLookupEntryPacked {
  // 1-based identifier of the first value.
  // 0 means "the identifier after the last value of the previous entry of
  // the same lookup table", or 1 in the first entry of the table.
  uint32 id = 1;
  // Values of the entries (UTF-8 encoded), for consecutive identifiers
  // starting at id.
  repeated string values = 2;
}

// ---------------------------------------------------------------------------
// Stream options
// ---------------------------------------------------------------------------

// RDF stream options
//
// If row layout: the options may occur again later in the stream, but must
// be identical to the first occurrence.
// If column layout: a later frame may restate the options, which empties the
// lookup tables. This makes streams concatenable. physical_type, stream_type,
// and version must stay the same.
message RdfStreamOptions {
  // Name of the stream (completely optional).
  // This may be used for, e.g., topic names in a pub/sub system.
  string stream_name = 1;
  // Type of the stream (required).
  // If row layout: TRIPLES, QUADS, or GRAPHS.
  // If column layout: TRIPLES or QUADS.
  PhysicalStreamType physical_type = 2;
  // If row layout: whether the stream may contain generalized triples,
  // quads, or datasets.
  // If column layout: must not be set.
  bool generalized_statements = 3;
  // If row layout: whether the stream may contain RDF-star statements.
  // If column layout: must not be set – use rdf_version instead.
  bool rdf_star = 4;
  // If column layout: whether the stream is one RDF graph or dataset, or a
  // stream of RDF Messages. Defaults to RDF_STREAM_TYPE_FLAT.
  // If row layout: must not be set.
  RdfStreamType stream_type = 5;
  // If column layout: version of RDF whose terms may occur in the stream.
  // Unspecified (the default) means that no version is announced, and the
  // stream may contain any RDF 1.2 term.
  // If row layout: must not be set.
  RdfVersion rdf_version = 6;
  // Maximum size of the name lookup table (required).
  // If row layout: must be >= 8.
  // If column layout: must be >= 128.
  uint32 max_name_table_size = 9;
  // Maximum size of the prefix lookup table
  // (required if the prefix lookup is used)
  uint32 max_prefix_table_size = 10;
  // Maximum size of the datatype lookup table
  // (required if datatype literals are used)
  uint32 max_datatype_table_size = 11;
  // If row layout: logical (RDF-STaX-based) stream type. In contrast to the
  // physical type, this field is entirely optional.
  // If column layout: must not be set – use stream_type instead.
  LogicalStreamType logical_type = 14;
  // Protocol version (required)
  // For Jelly-RDF 1.0.x the value must be 1 (row layout).
  // For Jelly-RDF 1.1.x the value must be 2 (row layout).
  // For Jelly-RDF 1.2.x the value must be 3 (column layout).
  // For custom extensions, the value must be 10000 or higher.
  uint32 version = 15;
}

// Physical stream type: whether the stream contains triples or quads.
enum PhysicalStreamType {
  // Unspecified stream type – invalid
  PHYSICAL_STREAM_TYPE_UNSPECIFIED = 0;
  // RDF triples
  PHYSICAL_STREAM_TYPE_TRIPLES = 1;
  // RDF quads
  PHYSICAL_STREAM_TYPE_QUADS = 2;
  // RDF triples grouped in graphs.
  // If column layout: invalid.
  PHYSICAL_STREAM_TYPE_GRAPHS = 3;
}

// Type of an RDF stream. Not used in the row layout.
enum RdfStreamType {
  // The entire stream is one RDF graph (TRIPLES) or one RDF dataset (QUADS).
  // This is the default.
  // buf:lint:ignore ENUM_ZERO_VALUE_SUFFIX
  RDF_STREAM_TYPE_FLAT = 0;
  // The stream is a sequence of RDF Messages, each an RDF graph or dataset:
  // https://w3c-cg.github.io/rsp/spec/messages
  // The messages are delimited by RdfColumnBatch.message_lengths.
  RDF_STREAM_TYPE_MESSAGES = 1;
}

// ---------------------------------------------------------------------------
// Stream frame
// ---------------------------------------------------------------------------

// RDF stream frame – base message for RDF streams.
message RdfStreamFrame {
  // Stream rows.
  // If row layout: the contents of the frame.
  // If column layout: either empty, or exactly one row with the stream
  // options.
  repeated RdfStreamRow rows = 1;
  // If column layout: the contents of the frame.
  // If row layout: must not be set.
  RdfColumnBatch columns = 2;
  // Arbitrary metadata
  // The keys are UTF-8 encoded strings, the values are byte arrays.
  // This may be used by implementations in any way they see fit.
  // The metadata does not affect the RDF data in any way, treat it
  // as comments in a text file.
  map<string, bytes> metadata = 15;
}

// ---------------------------------------------------------------------------
// Column layout
// ---------------------------------------------------------------------------

// The contents of a frame in the column layout: a batch of statements, and
// the lookup entries and namespace declarations they need.
//
// Each statement is made up of the cells at the same position in the
// subjects, predicates, objects, and graphs columns.
message RdfColumnBatch {
  // Number of statements in the batch. At most 2^27 - 1.
  uint32 row_count = 1;
  // Entries of the name lookup table.
  repeated RdfLookupEntryPacked names = 2;
  // Entries of the prefix lookup table.
  //
  // Note: the prefixes in the lookup table can be arbitrary strings, and are
  // NOT meant to be user-facing. They are only used for IRI compression.
  // To transmit user-facing namespace declarations, use `namespaces`.
  repeated RdfLookupEntryPacked prefixes = 3;
  // Entries of the datatype lookup table.
  repeated RdfLookupEntryPacked datatypes = 4;
  // Namespace declarations.
  repeated RdfNamespaceDeclaration namespaces = 5;
  // Subjects: IRIs or blank nodes. No cell may be unbound.
  RdfColumn subjects = 6;
  // Predicates: IRIs. No cell may be unbound.
  RdfColumn predicates = 7;
  // Objects: IRIs, blank nodes, literals, or triple terms. No cell may be
  // unbound.
  RdfColumn objects = 8;
  // Graph names: IRIs or blank nodes. An unbound cell is the default graph.
  // Only valid if the physical type is QUADS. If not set, all statements of
  // the batch are in the default graph.
  RdfColumn graphs = 9;
  // Message boundaries: each value adds that many more statements to the
  // current RDF Message, and then ends it.
  // Only valid in streams of type RDF_STREAM_TYPE_MESSAGES.
  repeated uint32 message_lengths = 15;
}

// Explicit namespace declaration
//
// This is a hint to the consumer that the given IRI (namespace) may be
// associated with a short name, like in Turtle syntax:
// PREFIX ex: <http://example.org/>
//
// This is a purely cosmetic feature, not part of the RDF data. It has nothing
// in common with the prefix lookup table. A declaration stays in effect until
// the same name is declared again.
message RdfNamespaceDeclaration {
  // Short name of the namespace (e.g., "ex")
  // Do NOT include the colon.
  string name = 1;
  // IRI of the namespace (e.g., "http://example.org/")
  RdfIri value = 2;
}

// ---------------------------------------------------------------------------
// Columns
// ---------------------------------------------------------------------------

// Column of RDF terms: the cells of one statement position (Jelly-RDF) or one
// variable (Jelly-SPARQL) across all rows of a batch, in row order. A cell is
// either an RDF term or unbound.
//
// Each run of consecutive equal terms is stored once, as a run value, and
// unbound cells are not stored. The run values are split by type into
// separate lists. `kinds` gives the type of each run value, and `layouts`
// gives the runs of repeated values and of unbound cells.
//
// Used by the column layout and by Jelly-SPARQL.
message RdfColumn {
  // Type of each run value, 2 bits per value, four values per byte, starting
  // from the least significant bits: 0 = IRI, 1 = literal, 2 = blank node,
  // 3 = triple term.
  // Should be empty if all run values are of one type.
  bytes kinds = 1;
  // Sequence layout: where the run values repeat, and where the unbound cells
  // are. Each entry is a token (skip << 5) | (kind << 4) | len_code, and a
  // len_code of 15 is followed by an extension varint.
  // Empty if every cell is bound and no value repeats.
  repeated uint32 layouts = 2;
  // Name identifiers of the IRIs, in row order.
  // 0 means "previous name_id + 1", or 1 in the first IRI of the column.
  repeated uint32 name_ids = 3;
  // Prefix identifiers of the IRIs: none (no prefix), one (the same prefix
  // for all IRIs), or one per IRI.
  // 0 means "the same prefix as the previous IRI", or no prefix in the first
  // IRI of the column.
  repeated uint32 prefix_ids = 4;
  // Lexical forms of the literals, in row order.
  repeated string lex_values = 5;
  // Literal kinds of the literals: none (all are simple literals), one (the
  // same kind for all literals), or one per literal.
  //   0          – simple literal (xsd:string)
  //   odd v      – literal with the datatype (v + 1) / 2 of the datatype
  //                lookup
  //   even v > 0 – language-tagged string with the language tag
  //                langtags[v / 2 - 1]
  repeated uint32 literal_kinds = 6;
  // Language tags referred to by literal_kinds.
  repeated string langtags = 7;
  // Base directions of the language tags (RdfBaseDirection values), parallel
  // to langtags. Empty means no base direction for any tag.
  repeated uint32 langtag_directions = 8;
  // Labels of the blank nodes, in row order.
  repeated string bnodes = 9;
  // Triple terms, in row order. The IRIs inside the triple terms are
  // inferred separately from name_ids and prefix_ids.
  repeated RdfTripleTerm triple_terms = 10;
}

// ---------------------------------------------------------------------------
// Row layout (Jelly-RDF 1.0.x and 1.1.x)
//
// The messages below are not used in the column layout. They are also used by
// Jelly-Patch.
// ---------------------------------------------------------------------------

// Empty message indicating the default RDF graph.
//
// Not used in the column layout.
message RdfDefaultGraph {}

// RDF triple
//
// For each term (subject, predicate, object), the fields are repeated for
// performance reasons. This is to avoid the need for boxing each term in a
// separate message.
//
// Note: this message allows for representing generalized RDF triples (for
// example, with literals as predicates). Whether this is used in the stream
// is determined by the stream options (see RdfStreamOptions).
//
// If no field in a given oneof is set, the term is interpreted as a repeated
// term – the same as the term in the same position in the previous triple.
// In the first triple of the stream, all terms must be set.
// All terms must also be set in quoted triples (RDF-star).
//
// Not used in the column layout.
message RdfTriple {
  // Triple subject
  oneof subject {
    // IRI
    RdfIri s_iri = 1;
    // Blank node
    string s_bnode = 2;
    // Literal
    // Only valid in a generalized RDF stream.
    RdfLiteral s_literal = 3;
    // RDF-star quoted triple
    RdfTriple s_triple_term = 4;
  }

  // Triple predicate
  oneof predicate {
    // IRI
    RdfIri p_iri = 5;
    // Blank node
    // Only valid in a generalized RDF stream.
    string p_bnode = 6;
    // Literal
    // Only valid in a generalized RDF stream.
    RdfLiteral p_literal = 7;
    // RDF-star quoted triple
    RdfTriple p_triple_term = 8;
  }

  // Triple object
  oneof object {
    // IRI
    RdfIri o_iri = 9;
    // Blank node
    string o_bnode = 10;
    // Literal
    RdfLiteral o_literal = 11;
    // RDF-star quoted triple
    RdfTriple o_triple_term = 12;
  }
}

// RDF quad
//
// Fields 1–12 are repeated from RdfTriple for performance reasons.
//
// Similarly to RdfTriple, this message allows for representing generalized
// RDF quads (for example, with literals as predicates). Whether this is used
// in the stream is determined by the stream options (see RdfStreamOptions).
//
// If no field in a given oneof is set, the term is interpreted as a repeated
// term – the same as the term in the same position in the previous quad.
// In the first quad of the stream, all terms must be set.
//
// Not used in the column layout.
message RdfQuad {
  // Quad subject
  oneof subject {
    // IRI
    RdfIri s_iri = 1;
    // Blank node
    string s_bnode = 2;
    // Literal
    // Only valid in a generalized RDF stream.
    RdfLiteral s_literal = 3;
    // RDF-star quoted triple
    RdfTriple s_triple_term = 4;
  }

  // Quad predicate
  oneof predicate {
    // IRI
    RdfIri p_iri = 5;
    // Blank node
    // Only valid in a generalized RDF stream.
    string p_bnode = 6;
    // Literal
    // Only valid in a generalized RDF stream.
    RdfLiteral p_literal = 7;
    // RDF-star quoted triple
    RdfTriple p_triple_term = 8;
  }

  // Quad object
  oneof object {
    // IRI
    RdfIri o_iri = 9;
    // Blank node
    string o_bnode = 10;
    // Literal
    RdfLiteral o_literal = 11;
    // RDF-star quoted triple
    RdfTriple o_triple_term = 12;
  }

  // Quad graph
  oneof graph {
    // IRI
    RdfIri g_iri = 13;
    // Blank node
    string g_bnode = 14;
    // Default graph
    RdfDefaultGraph g_default_graph = 15;
    // Literal – only valid for generalized RDF streams
    RdfLiteral g_literal = 16;
  }
}

// Start of a graph in a GRAPHS stream
//
// In contrast to RdfQuad, setting the graph oneof to some value
// is always required. No repeated terms are allowed.
//
// Not used in the column layout.
message RdfGraphStart {
  oneof graph {
    // IRI
    RdfIri g_iri = 1;
    // Blank node
    string g_bnode = 2;
    // Default graph
    RdfDefaultGraph g_default_graph = 3;
    // Literal – only valid for generalized RDF streams
    RdfLiteral g_literal = 4;
  }
}

// End of a graph in a GRAPHS stream
//
// Not used in the column layout.
message RdfGraphEnd {}

// Entry in the name lookup table
//
// Not used in the column layout.
message RdfNameEntry {
  // 1-based identifier
  // If id=0, it should be interpreted as previous_id + 1.
  // If id=0 appears in the first RdfNameEntry of the stream, it should be
  // interpreted as 1.
  uint32 id = 1;
  // Value of the name (UTF-8 encoded)
  string value = 2;
}

// Entry in the prefix lookup table
//
// Note: the prefixes in the lookup table can be arbitrary strings, and are
// NOT meant to be user-facing. They are only used for IRI compression.
// To transmit user-facing namespace declarations for cosmetic purposes, use
// RdfNamespaceDeclaration.
//
// Not used in the column layout.
message RdfPrefixEntry {
  // 1-based identifier
  // If id=0, it should be interpreted as previous_id + 1.
  // If id=0 appears in the first RdfPrefixEntry of the stream, it should be
  // interpreted as 1.
  uint32 id = 1;
  // Value of the prefix (UTF-8 encoded)
  string value = 2;
}

// Entry in the datatype lookup table
//
// Not used in the column layout.
message RdfDatatypeEntry {
  // 1-based identifier
  // If id=0, it should be interpreted as previous_id + 1.
  // If id=0 appears in the first RdfDatatypeEntry of the stream, it should be
  // interpreted as 1.
  uint32 id = 1;
  // Value of the datatype (UTF-8 encoded)
  string value = 2;
}

// Logical stream type, according to the RDF Stream Taxonomy (RDF-STaX).
// If column layout: not used – use RdfStreamType instead.
//
// Type 0 is reserved for the unspecified stream type.
// The rest of the type numbers follow the taxonomical structure of RDF-STaX.
// For example: 1 is a subtype of 0, 13 and 23 are subtypes of 3,
// 114 is a subtype of 14, etc.
//
// Types 1–4 correspond to the four base concrete stream types. Their
// subtypes can be in most cases simply processed in the same way as
// the base types.
// Therefore, implementations can take the modulo 10 of the stream
// type to determine the base type of the stream and use this information
// to select the appropriate processing logic.
//
// RDF-STaX version: 1.1.2
// https://w3id.org/stax/1.1.2
//
// Not used in the column layout.
enum LogicalStreamType {
  // Unspecified stream type – invalid
  LOGICAL_STREAM_TYPE_UNSPECIFIED = 0;
  // Flat RDF triple stream
  // https://w3id.org/stax/ontology#flatTripleStream
  LOGICAL_STREAM_TYPE_FLAT_TRIPLES = 1;
  // Flat RDF quad stream
  // https://w3id.org/stax/ontology#flatQuadStream
  LOGICAL_STREAM_TYPE_FLAT_QUADS = 2;
  // RDF graph stream
  // https://w3id.org/stax/ontology#graphStream
  LOGICAL_STREAM_TYPE_GRAPHS = 3;
  // RDF dataset stream
  // https://w3id.org/stax/ontology#datasetStream
  LOGICAL_STREAM_TYPE_DATASETS = 4;

  // RDF subject graph stream (subtype of RDF graph stream)
  // https://w3id.org/stax/ontology#subjectGraphStream
  LOGICAL_STREAM_TYPE_SUBJECT_GRAPHS = 13;

  // RDF named graph stream (subtype of RDF dataset stream)
  // https://w3id.org/stax/ontology#namedGraphStream
  LOGICAL_STREAM_TYPE_NAMED_GRAPHS = 14;
  // RDF timestamped named graph stream (subtype of RDF dataset stream)
  // https://w3id.org/stax/ontology#timestampedNamedGraphStream
  LOGICAL_STREAM_TYPE_TIMESTAMPED_NAMED_GRAPHS = 114;
}

// RDF stream row
//
// If row layout: one element of a frame.
// If column layout: only the `options` field may be set.
message RdfStreamRow {
  // Exactly one of these fields must be set.
  oneof row {
    // Stream options. Must occur at the start of the stream.
    RdfStreamOptions options = 1;
    // RDF triple statement.
    // Valid in streams of physical type TRIPLES or GRAPHS.
    RdfTriple triple = 2;
    // RDF quad statement.
    // Only valid in streams of physical type QUADS.
    RdfQuad quad = 3;
    // Graph boundary: ends the currently transmitted graph and starts a new one
    // Only valid in streams of physical type GRAPHS.
    RdfGraphStart graph_start = 4;
    // Explicit end of a graph.
    // Signals the consumer that the transmitted graph is complete.
    // Only valid in streams of physical type GRAPHS.
    RdfGraphEnd graph_end = 5;
    // Explicit namespace declaration.
    RdfNamespaceDeclaration namespace = 6;
    // Entry in the name lookup table.
    RdfNameEntry name = 9;
    // Entry in the prefix lookup table.
    RdfPrefixEntry prefix = 10;
    // Entry in the datatype lookup table.
    RdfDatatypeEntry datatype = 11;
  }
}

sparql.proto

syntax = "proto3";
package eu.ostrzyciel.jelly.core.proto.v1.sparql;

// Jelly-SPARQL: binary serialization for SPARQL query results
// (solution sequences and boolean ASK results), based on the Jelly-RDF term
// encoding (rdf.proto).
// Specification document:
// https://w3id.org/jelly/1.2.0/specification/sparql
// Protocol version: 1.0.0
//
// The results are transmitted as a stream of frames. Each frame contains a
// batch of solutions (rows) in a columnar layout: one column per variable.
// The columns (RdfColumn) and their sequence layout encoding are defined in
// rdf.proto, and shared with the column layout of Jelly-RDF.
//
// The first frame has the header: stream options and the variable names, one
// per column. The stream ends with a trailer, which says whether the result
// set is complete.
//
// By default, a stream contains one result set (SPARQL_STREAM_TYPE_FLAT). A
// stream of type SPARQL_STREAM_TYPE_PUNCTUATED contains a sequence of result
// sets instead, each ended by a trailer.

import "rdf.proto";

// Stream options for SPARQL result streams.
message SparqlResultsOptions {
  // Name of the stream (completely optional).
  // This may be used for, e.g., topic names in a pub/sub system.
  string stream_name = 1;
  // Type of the stream: one result set, or a sequence of result sets.
  // Defaults to SPARQL_STREAM_TYPE_FLAT.
  SparqlStreamType stream_type = 2;
  // Version of RDF whose terms may occur in the stream.
  //   RDF_VERSION_1_1       – no triple terms, no base directions
  //   RDF_VERSION_1_2_BASIC – base directions, no triple terms
  //   RDF_VERSION_1_2       – base directions and triple terms
  // Unspecified (the default) means that no version is announced: the stream
  // may contain any RDF 1.2 term.
  RdfVersion rdf_version = 5;
  // Maximum size of the name lookup table
  // (required, must be >= 128)
  uint32 max_name_table_size = 9;
  // Maximum size of the prefix lookup table
  // (required if the prefix lookup is used)
  uint32 max_prefix_table_size = 10;
  // Maximum size of the datatype lookup table
  // (required if datatype literals are used)
  uint32 max_datatype_table_size = 11;
  // Protocol version (required)
  // For Jelly-SPARQL 1.0.x, the value must be 1.
  // For custom extensions, the value must be 10000 or higher.
  uint32 version = 15;
}

// Type of a SPARQL result stream.
enum SparqlStreamType {
  // The entire stream is a single result set. This is the default.
  // buf:lint:ignore ENUM_ZERO_VALUE_SUFFIX
  SPARQL_STREAM_TYPE_FLAT = 0;
  // The stream is a sequence of result sets, each ended by a frame with a
  // trailer. The lookup tables are kept from one result set to the next.
  SPARQL_STREAM_TYPE_PUNCTUATED = 1;
}

// Boolean result of a SPARQL ASK query.
message SparqlAskResult {
  // The boolean value of the result.
  bool value = 1;
}

// End-of-result-set marker, saying whether the result set is complete.
// Producers must write it in the last frame of every result set, unless they
// cannot write anything more at all (for example, because the process died).
message SparqlResultsTrailer {
  // Empty (the default) means the result set is complete.
  // A non-empty value is a human-readable explanation of why the producer
  // could not complete the result set (e.g., query failure mid-evaluation).
  string error = 1;
}

// SPARQL results frame – base message of a SPARQL result stream.
// A frame contains a batch of rows (solutions).
message SparqlResultsFrame {
  // Stream options.
  // Must be set in the first frame of the stream. May be set again in a
  // later frame, which empties the lookup tables.
  SparqlResultsOptions options = 1;
  // Result set header: the names of the variables, in projection (SELECT
  // clause) order, without the leading "?" or "$".
  // Set in the first frame of a result set with a solution sequence, and in
  // a frame that contains the options.
  repeated string variables = 2;
  // Number of rows (solutions) in this frame. At most 2^27 - 1.
  uint32 row_count = 3;
  // Entries in the name lookup table.
  repeated RdfLookupEntryPacked names = 4;
  // Entries in the prefix lookup table.
  //
  // Note: the prefixes in the lookup table can be arbitrary strings, and are
  // NOT meant to be user-facing. They are only used for IRI compression.
  repeated RdfLookupEntryPacked prefixes = 5;
  // Entries in the datatype lookup table.
  repeated RdfLookupEntryPacked datatypes = 6;
  // Columns, one per variable, in the order of `variables`, or none.
  repeated RdfColumn columns = 7;
  // Boolean (ASK) result.
  // If set, the result set is a boolean result instead of a solution
  // sequence. A boolean result is always the only frame of its result set.
  SparqlAskResult ask_result = 8;
  // End-of-result-set marker. See SparqlResultsTrailer.
  SparqlResultsTrailer trailer = 9;
  // Arbitrary metadata
  // The keys are UTF-8 encoded strings, the values are byte arrays.
  // The metadata does not affect the result data in any way, treat it
  // as comments in a text file.
  //
  // Well-known keys (their values must be valid UTF-8):
  //   link – zero or more IRIs separated by LF (U+000A), describing the
  //          result set as a whole (not the frame). Corresponds to head.link
  //          in the SPARQL Query Results JSON format.
  //
  // All other keys may be used by implementations in any way they see fit,
  // and their values are arbitrary bytes.
  map<string, bytes> metadata = 15;
}

patch.proto

syntax = "proto3";
package eu.ostrzyciel.jelly.core.proto.v1.patch;

// EXPERIMENTAL FEATURE -- the API and format may change.

// RDF Patch implementation based on Jelly
// RDF Patch spec: https://afs.github.io/rdf-delta/rdf-patch.html
// Jelly-Patch spec: https://w3id.org/jelly/1.1.0/specification/patch

import "rdf.proto";

message RdfPatchTransactionStart {}

message RdfPatchTransactionCommit {}

message RdfPatchTransactionAbort {}

message RdfPatchNamespace {
  // Short name of the namespace (e.g., "ex")
  // Do NOT include the colon.
  string name = 1;
  // IRI of the namespace (e.g., "http://example.org/").
  // Optional in the "prefix delete" (PD) operation,
  // but required in the "prefix add" (PA) operation.
  RdfIri value = 2;
  // Graph name for the namespace declaration.
  //
  // This is required for the statement type QUADS, and disallowed for
  // the statement type TRIPLES.
  //
  // This is used to achieve full 1:1 replication in systems that associate
  // namespaces with specific graphs (e.g., Apache Jena).
  //
  // Repeated term compression IS supported for this field.
  // The repeated terms are shared between the namespace and statement rows.
  oneof graph {
    // IRI
    RdfIri g_iri = 3;
    // Blank node
    string g_bnode = 4;
    // Default graph
    RdfDefaultGraph g_default_graph = 5;
    // Literal – only valid for generalized RDF streams
    RdfLiteral g_literal = 6;
  }
}

// Metadata header for RDF Patch.
// Header rows must occur at the start of the patch frame.
message RdfPatchHeader {
  // Header key (required)
  string key = 1;
  // Header value (required)
  // Repeated term compression is not supported for header values.
  // This oneof must always be set to a valid RDF term.
  oneof value {
    // IRI
    RdfIri h_iri = 2;
    // Blank node
    string h_bnode = 3;
    // Literal
    RdfLiteral h_literal = 4;
    // RDF-star quoted triple
    RdfTriple h_triple_term = 5;
  }
}

message RdfPatchPunctuation {}

// RDF Patch stream options
message RdfPatchOptions {
  // Type of statements in the stream (required)
  PatchStatementType statement_type = 1;
  // Type of the stream (required)
  PatchStreamType stream_type = 2;
  // Whether the stream may contain generalized triples, quads, or datasets
  bool generalized_statements = 3;
  // Whether the stream may contain RDF-star statements
  bool rdf_star = 4;
  // Maximum size of the name lookup table
  // (required, must be >= 8)
  uint32 max_name_table_size = 9;
  // Maximum size of the prefix lookup table
  // (required if the prefix lookup is used)
  uint32 max_prefix_table_size = 10;
  // Maximum size of the datatype lookup table
  // (required if datatype literals are used)
  uint32 max_datatype_table_size = 11;
  // Protocol version (required)
  // For Jelly-Patch 1.0.x (based on Jelly-RDF 1.1.x), the value must be 1.
  // For custom extensions, the value must be 10000 or higher.
  uint32 version = 15;
}

// Type of statements in the stream.
// This determines whether the statements are interpreted as triples or quads.
enum PatchStatementType {
  // Unspecified stream type – invalid
  PATCH_STATEMENT_TYPE_UNSPECIFIED = 0;
  // RDF triples
  PATCH_STATEMENT_TYPE_TRIPLES = 1;
  // RDF quads
  PATCH_STATEMENT_TYPE_QUADS = 2;
}

// Type of the stream.
// This determines whether each RdfPatchFrame is a complete patch by itself,
// or part of a larger patch.
enum PatchStreamType {
  // Unspecified stream type – invalid
  PATCH_STREAM_TYPE_UNSPECIFIED = 0;
  // Each RdfPatchFrame is a complete patch.
  // In this case, transactions cannot span multiple frames.
  PATCH_STREAM_TYPE_FRAME = 1;
  // Entire stream is a single patch.
  // Transactions may span multiple frames.
  PATCH_STREAM_TYPE_FLAT = 2;
  // Stream contains multiple patches separated by punctuation marks.
  // Patches and transactions may span multiple frames.
  // Each frame must contain at most one patch.
  PATCH_STREAM_TYPE_PUNCTUATED = 3;
}

// A row in an RDF Patch stream
message RdfPatchRow {
  oneof row {
    // Stream options. Must be the first row in the stream.
    RdfPatchOptions options = 1;
    // Add a triple/quad statement (A)
    RdfQuad statement_add = 2;
    // Delete a triple/quad statement (D)
    RdfQuad statement_delete = 3;
    // Add a namespace/prefix declaration (PA)
    RdfPatchNamespace namespace_add = 4;
    // Delete a namespace/prefix declaration (PD)
    RdfPatchNamespace namespace_delete = 5;
    // Transaction start (TX)
    RdfPatchTransactionStart transaction_start = 6;
    // Transaction commit (TC)
    RdfPatchTransactionCommit transaction_commit = 7;
    // Transaction abort (TA)
    RdfPatchTransactionAbort transaction_abort = 8;
    // Entry in the name lookup table.
    RdfNameEntry name = 11;
    // Entry in the prefix lookup table.
    RdfPrefixEntry prefix = 12;
    // Entry in the datatype lookup table.
    RdfDatatypeEntry datatype = 13;
    // Header entry (H)
    RdfPatchHeader header = 14;
    // Punctuation mark, only used in stream type PUNCTUATED
    // Indicates the end of a patch.
    RdfPatchPunctuation punctuation = 15;
  }
}

// A batch of RDF patch rows.
message RdfPatchFrame {
  // Patch rows
  repeated RdfPatchRow rows = 1;
}

grpc.proto

syntax = "proto3";
package eu.ostrzyciel.jelly.core.proto.v1;

// Jelly gRPC streaming protocol.
// Specification document: https://w3id.org/jelly/1.0.0/specification/streaming
// Protocol version: 1.0.0

import "rdf.proto";

// Subscribe command sent by the client to the server.
message RdfStreamSubscribe {
  // The topic to which the client wants to subscribe (UTF-8 encoded).
  string topic = 1;
  // Optional: the stream options requested by the client.
  // The server should respond with a stream that matches these options.
  // In case that is not possible, the server must respond with the
  // INVALID_ARGUMENT error.
  RdfStreamOptions requested_options = 2;
}

// Acknowledgement of receiving a stream sent by the server to the client.
message RdfStreamReceived {}

// Pub/Sub service for RDF streams, to be implemented by the server.
service RdfStreamService {
  // Subscribe to an RDF stream.
  rpc SubscribeRdf(RdfStreamSubscribe) returns (stream RdfStreamFrame);
  // Publish an RDF stream.
  // In case the server cannot process the stream, it must respond with
  // the INVALID_ARGUMENT error.
  rpc PublishRdf(stream RdfStreamFrame) returns (RdfStreamReceived);
}

See also