Extension
Extension service API reference
Services
ExtensionService Service
Service for managing Extensions in the GoodMem system.
Extensions are JAR-based plugins that enhance GoodMem functionality with post-processors and custom logic components. This service provides lifecycle management including upload, download, metadata retrieval, listing, and deletion operations.
Authentication: gRPC metadata authorization: Bearer <api-key>
Global errors: All RPCs may return DEADLINE_EXCEEDED, CANCELLED, UNAVAILABLE, RESOURCE_EXHAUSTED, INTERNAL.
Permissions model:
*_EXTENSION_OWN: operate on caller-owned extensions*_EXTENSION_ANY: operate on any user's extensions (requires elevated role)
Security considerations:
- Binary JAR content is separated from metadata for security and performance
- Download operations require separate permissions from metadata access
- All plugin content is integrity-verified with Murmur3 hashing
CreateExtension
Creates a new Extension by uploading a plugin file.
| Type | |
|---|---|
| Request | goodmem.v1.CreateExtensionRequest |
| Response | goodmem.v1.Extension |
Auth: gRPC metadata authorization: Bearer <api-key>
Permissions Required: CREATE_EXTENSION_OWN or CREATE_EXTENSION_ANY
Summary:
- Owner defaults to the authenticated user unless
owner_idis provided (requires*_ANYif differs) ALREADY_EXISTS: another extension exists with identical{owner_id, extension_type, display_name}combination- Calculates Murmur3 32-bit x86 hash (8-char lowercase hex, seed=0) for corruption detection
- Sets default
statusto"ACTIVE"andmedia_typeto"application/java-archive"if not provided
Side Effects:
- Persists extension with binary content; sets audit fields
Error Codes:
UNAUTHENTICATED: missing/invalid authPERMISSION_DENIED: lacksCREATE_EXTENSION_*INVALID_ARGUMENT: empty/invalid fields; unsupportedextension_type; emptyplugin_content; file size exceeds server limitsALREADY_EXISTS: matching extension as defined aboveINTERNAL: unexpected server error
Idempotency: Non-idempotent; clients SHOULD NOT blindly retry on unknown failures.
Examples:
grpcurl -plaintext \
-H 'authorization: Bearer gm_xxx' \
-d '{
"display_name": "Custom Processor",
"extension_type": "JAVA_PROCESSOR",
"filename": "my-processor.jar",
"plugin_content": "BASE64_JAR_CONTENT_HERE"
}' \
localhost:8080 goodmem.v1.ExtensionService/CreateExtensionNote: bytes fields in JSON must be base64.
GetExtension
Retrieves metadata of a specific Extension (without plugin content).
| Type | |
|---|---|
| Request | goodmem.v1.GetExtensionRequest |
| Response | goodmem.v1.Extension |
Auth: gRPC metadata authorization: Bearer <api-key>
Permissions Required: READ_EXTENSION_OWN or READ_EXTENSION_ANY
Side Effects: None
Error Codes:
UNAUTHENTICATED: missing/invalid authPERMISSION_DENIED: lacksREAD_EXTENSION_*INVALID_ARGUMENT: invalid extension ID formatNOT_FOUND: extension does not existINTERNAL: unexpected server error
Idempotency: Read-only; safe to retry; results may change over time.
Examples:
grpcurl -plaintext \
-H 'authorization: Bearer gm_xxx' \
-d '{ "extension_id": "BASE64_UUID_BYTES_HERE" }' \
localhost:8080 goodmem.v1.ExtensionService/GetExtensionNote: bytes fields in JSON must be base64.
DownloadExtension
Downloads the plugin content of a specific Extension.
| Type | |
|---|---|
| Request | goodmem.v1.DownloadExtensionRequest |
| Response | goodmem.v1.DownloadExtensionResponse |
Auth: gRPC metadata authorization: Bearer <api-key>
Permissions Required: DOWNLOAD_EXTENSION_OWN or DOWNLOAD_EXTENSION_ANY
Security:
- Returns binary JAR content with integrity hash for verification
- Separate download permission required from metadata access
- Download permissions are independent of extension status (
ACTIVE/INACTIVE/DISABLED) - This allows downloading for debugging/forensic purposes even when extensions are disabled
Side Effects: None
Error Codes:
UNAUTHENTICATED: missing/invalid authPERMISSION_DENIED: lacksDOWNLOAD_EXTENSION_*INVALID_ARGUMENT: invalid extension ID formatNOT_FOUND: extension does not existINTERNAL: unexpected server error
Idempotency: Read-only; safe to retry; content is immutable.
Examples:
grpcurl -plaintext \
-H 'authorization: Bearer gm_xxx' \
-d '{ "extension_id": "BASE64_UUID_BYTES_HERE" }' \
localhost:8080 goodmem.v1.ExtensionService/DownloadExtensionNote: bytes fields in JSON must be base64.
ListExtensions
Lists Extensions accessible to the authenticated user.
| Type | |
|---|---|
| Request | goodmem.v1.ListExtensionsRequest |
| Response | goodmem.v1.ListExtensionsResponse |
Auth: gRPC metadata authorization: Bearer <api-key>
Permissions Required: LIST_EXTENSION_OWN or LIST_EXTENSION_ANY
Request Parameters:
owner_id(optional, bytes UUID): filter by owner. If caller hasLIST_EXTENSION_ANYandowner_idis omitted, all extensions visible to the role are returned; otherwise only caller-owned extensions are returnedname_filter(optional): glob pattern matching ondisplay_name; supports*(any chars),?(single char),\(escape); full-string match; case-sensitive- Pagination: simple offset-based with opaque pagination tokens
- Sorting: by
created_atdescending (newest first)
Note: bytes fields in JSON must be base64.
Side Effects: None
Error Codes:
UNAUTHENTICATED: missing/invalid authPERMISSION_DENIED: lacksLIST_EXTENSION_*INVALID_ARGUMENT: invalid filters or parametersINTERNAL: unexpected server error
Idempotency: Read-only; safe to retry; results may change over time.
Examples:
grpcurl -plaintext \
-H 'authorization: Bearer gm_xxx' \
-d '{ "name_filter": "processor*", "max_results": 10 }' \
localhost:8080 goodmem.v1.ExtensionService/ListExtensionsDeleteExtension
Permanently deletes an Extension and its binary content.
| Type | |
|---|---|
| Request | goodmem.v1.DeleteExtensionRequest |
| Response | google.protobuf.Empty |
Auth: gRPC metadata authorization: Bearer <api-key>
Permissions Required: DELETE_EXTENSION_OWN or DELETE_EXTENSION_ANY
Side Effects:
- Removes the extension record and binary JAR content permanently
Error Codes:
UNAUTHENTICATED: missing/invalid authPERMISSION_DENIED: lacksDELETE_EXTENSION_*INVALID_ARGUMENT: invalid extension ID formatNOT_FOUND: extension does not existINTERNAL: unexpected server error
Idempotency: Safe to retry; may return NOT_FOUND if already deleted or never existed.
Examples:
grpcurl -plaintext \
-H 'authorization: Bearer gm_xxx' \
-d '{ "extension_id": "BASE64_UUID_BYTES_HERE" }' \
localhost:8080 goodmem.v1.ExtensionService/DeleteExtensionNote: bytes fields in JSON must be base64.
Messages
Extension
Represents a plugin (JAR file) that extends GoodMem functionality.
Extensions are JAR files containing post-processors, custom logic, or other plugin implementations that integrate with the GoodMem system. Each extension has metadata, security constraints, and lifecycle management through admin-controlled status transitions.
Security:
plugin_content(binary JAR data) is intentionally excluded from this message for security and performance reasons. UseDownloadExtensionto retrieve binary content.
Immutability:
extension_typeis effectively immutable (no update operations supported).owner_idis set at creation and cannot be modified.
Duplicate Prevention:
- System prevents duplicate extensions with identical
{owner_id, extension_type, display_name}. display_namecomparison is case-sensitive after leading/trailing whitespace trimming (no Unicode normalization applied).
Notes:
- All timestamps are UTC (
google.protobuf.Timestamp). - Murmur3 32-bit hash provides corruption detection for JAR content (not cryptographically secure).
See also: ExtensionService for management operations
| Field | Type | Description |
|---|---|---|
extension_id | bytes | OUTPUT_ONLY UUID (16 bytes); immutable primary identifier |
display_name | string | REQUIRED on create; ≤255 chars; leading/trailing whitespace trimmed; cannot be empty |
description | string | OPTIONAL description of extension functionality |
extension_type | string | REQUIRED extension type string; current valid values: "JAVA_PROCESSOR" |
filename | string | REQUIRED original filename of uploaded JAR; ≤255 UTF-8 bytes; cannot be empty after trimming; no path separators or control characters |
media_type | string | OUTPUT_ONLY MIME type; always non-empty; defaults to "application/java-archive" if not provided at create |
file_size | int64 | OUTPUT_ONLY size in bytes of JAR content; set during creation |
file_hash | string | OUTPUT_ONLY Murmur3 32-bit x86 hash (8-char lowercase hex, seed=0) for corruption detection only (not cryptographically secure); calculated during creation |
status | string | OUTPUT_ONLY lifecycle status; valid values: "ACTIVE", "INACTIVE", "DISABLED"; defaults to "ACTIVE"; admin-controlled via tooling only (no RPC for status changes); typical transitions: ACTIVE↔INACTIVE, →DISABLED |
owner_id | bytes | OUTPUT_ONLY owner UUID (16 bytes); set at create; not updatable |
created_at | google.protobuf.Timestamp | Standard audit fields OUTPUT_ONLY |
updated_at | google.protobuf.Timestamp | OUTPUT_ONLY |
created_by_id | bytes | OUTPUT_ONLY creator UUID (16 bytes) |
updated_by_id | bytes | OUTPUT_ONLY last updater UUID (16 bytes) |
CreateExtensionRequest
| Field | Type | Description |
|---|---|---|
extension_id | bytes | Optional client-provided UUID (16 bytes); server generates if omitted; returns ALREADY_EXISTS if ID exists |
display_name | string | Required: User-facing name (≤255 chars; leading/trailing whitespace trimmed; cannot be empty) |
description | string | Optional description of extension functionality |
extension_type | string | Required: Extension type string; current valid values: "JAVA_PROCESSOR" |
filename | string | Required: Original filename (≤255 UTF-8 bytes; cannot be empty after trimming; no path separators or control characters) |
media_type | string | Optional: MIME type (lowercased); empty defaults to "application/java-archive"; must be valid IANA type if provided |
plugin_content | bytes | Required: Binary JAR file content (cannot be empty; subject to server configuration, default limit 100MB) |
owner_id | bytes | Optional owner ID (16 bytes UUID); if omitted → authenticated user; requires CREATE_EXTENSION_ANY if different from caller |
GetExtensionRequest
| Field | Type | Description |
|---|---|---|
extension_id | bytes | Required: Extension ID (16 bytes UUID) |
DownloadExtensionRequest
| Field | Type | Description |
|---|---|---|
extension_id | bytes | Required: Extension ID (16 bytes UUID) |
DownloadExtensionResponse
| Field | Type | Description |
|---|---|---|
filename | string | Original filename of the JAR file |
media_type | string | MIME type (e.g., "application/java-archive") |
file_size | int64 | Size in bytes of `plugin_content` |
file_hash | string | Murmur3 32-bit x86 hash (8-char lowercase hex, seed=0) for corruption detection only (not cryptographically secure) |
plugin_content | bytes | Binary JAR file content |
ListExtensionsRequest
| Field | Type | Description |
|---|---|---|
owner_id | bytes | Optional filters Optional: Filter by owner (16 bytes UUID) |
name_filter | string | Optional: Glob pattern for display_name matching; supports * (any chars), ? (single char), \ (escape); full-string match; case-sensitive |
max_results | int32 | Pagination Optional: Max results per page; defaults to 50 if not provided or ≤0 |
next_token | string | Optional: Opaque pagination token; do not parse |
sort_by | string | Sorting (parameters currently ignored by implementation) Optional: Sort field name; IGNORED - implementation always sorts by "created_at" |
sort_order | goodmem.v1.SortOrder | Optional: Sort direction; IGNORED - implementation always uses DESCENDING |
ListExtensionsResponse
| Field | Type | Description |
|---|---|---|
extensions | goodmem.v1.Extension | Page of extension results (metadata only, no binary content) |
next_token | string | Opaque token for next page; omitted on final page |
ListExtensionsNextPageToken
INTERNAL: Reserved message for future pagination token structure.
Current implementation uses simple offset encoding. This message definition is reserved for potential future enhancement to stateful pagination with filter validation. Clients should treat pagination tokens as opaque strings.
| Field | Type | Description |
|---|---|---|
start | int32 | Reserved: cursor offset position |
owner_id | bytes | Reserved: owner filter from original request |
name_filter | string | Reserved: name filter pattern from original request |
requestor_id | bytes | Reserved: authenticated user ID for token validation |
sort_by | string | Reserved: sort field from original request |
sort_order | goodmem.v1.SortOrder | Reserved: sort direction from original request |
DeleteExtensionRequest
| Field | Type | Description |
|---|---|---|
extension_id | bytes | Required: Extension ID to delete (16 bytes UUID) |