Managing Policy
Policy in OpenTDF is the set of rules that govern who can access data and under what conditions. It is made up of namespaces, attributes, subject mappings, and subject condition sets. The SDK provides CRUD access to these policy rules through remote gRPC calls powered by the platform service client.
Namespaces
A namespace is the top-level organizational unit for attributes. Attribute FQNs are scoped to a namespace URI (e.g., https://example.com).
Namespaces are deactivated, not permanently deleted. There is no hard-delete operation and no restore operation exposed via the SDK.
Creating a Namespace
- Go
- Java
- TypeScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/namespaces"
"github.com/opentdf/platform/sdk"
)
func main() {
platformEndpoint := "http://localhost:8080"
// Create a new client
client, err := sdk.New(
platformEndpoint,
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
// Create Namespace
namespace := &namespaces.CreateNamespaceRequest{
Name: "opentdf.io",
}
_, err = client.Namespaces.CreateNamespace(context.Background(), namespace)
if err != nil {
log.Fatal(err)
}
}
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.namespaces.CreateNamespaceRequest;
import io.opentdf.platform.policy.namespaces.CreateNamespaceResponse;
import io.opentdf.platform.sdk.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.Objects;
public class CreateNamespace {
private static final Logger logger = LogManager.getLogger(CreateNamespace.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
String namespaceName = "opentdf.io";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
CreateNamespaceRequest createNamespaceRequest =
CreateNamespaceRequest.newBuilder().setName(namespaceName).build();
CreateNamespaceResponse createNamespaceResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.namespaces()
.createNamespaceBlocking(createNamespaceRequest, Collections.emptyMap())
.execute());
logger.info(
"Successfully created namespace with ID: {}",
createNamespaceResponse.getNamespace().getId());
} catch (Exception e) {
if (Objects.equals(e.getMessage(), "resource unique field violation")) {
logger.error("Namespace '{}' already exists", namespaceName, e);
} else {
logger.error("Failed to create namespace", e);
}
}
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
const platform = new PlatformClient({
authProvider,
platformUrl: 'http://localhost:8080',
});
// Create Namespace
const resp = await platform.v1.namespace.createNamespace({
name: 'opentdf.io',
});
console.log('Created namespace:', resp.namespace?.id);
List Namespaces
- Go
- Java
- TypeScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/namespaces"
"github.com/opentdf/platform/sdk"
)
func main() {
platformEndpoint := "http://localhost:8080"
// Create a new client
client, err := sdk.New(
platformEndpoint,
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
// List All Namespaces
namespaces, err := client.Namespaces.ListNamespaces(context.Background(), &namespaces.ListNamespacesRequest{})
if err != nil {
log.Fatal(err)
}
for _, namespace := range namespaces.GetNamespaces() {
log.Printf("Namespace: %s\n", namespace.GetName())
}
}
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.Namespace;
import io.opentdf.platform.policy.namespaces.ListNamespacesRequest;
import io.opentdf.platform.policy.namespaces.ListNamespacesResponse;
import io.opentdf.platform.sdk.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class ListNamespaces {
private static final Logger logger = LogManager.getLogger(ListNamespaces.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
ListNamespacesRequest request = ListNamespacesRequest.newBuilder().build();
ListNamespacesResponse listNamespacesResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.namespaces()
.listNamespacesBlocking(request, Collections.emptyMap())
.execute());
List<Namespace> namespaces = listNamespacesResponse.getNamespacesList();
logger.info(
"Successfully retrieved namespaces: [{}]",
namespaces.stream().map(Namespace::getFqn).collect(Collectors.joining(", ")));
} catch (Exception e) {
logger.error("Failed to list namespaces", e);
}
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
const platform = new PlatformClient({
authProvider,
platformUrl: 'http://localhost:8080',
});
// List All Namespaces
const resp = await platform.v1.namespace.listNamespaces({});
for (const ns of resp.namespaces) {
console.log('Namespace:', ns.name);
}
Get a Namespace
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/namespaces"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
// Look up by UUID
resp, err := client.Namespaces.GetNamespace(context.Background(),
&namespaces.GetNamespaceRequest{
Identifier: &namespaces.GetNamespaceRequest_NamespaceId{
NamespaceId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
},
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Namespace: %s (ID: %s)\n", resp.GetNamespace().GetName(), resp.GetNamespace().GetId())
// Alternatively, look up by FQN
respByFqn, err := client.Namespaces.GetNamespace(context.Background(),
&namespaces.GetNamespaceRequest{
Identifier: &namespaces.GetNamespaceRequest_Fqn{
Fqn: "https://example.com",
},
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Namespace by FQN: %s\n", respByFqn.GetNamespace().GetName())
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.namespaces.GetNamespaceRequest;
import java.util.Collections;
public class GetNamespaceExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
// Look up by UUID
var req = GetNamespaceRequest.newBuilder()
.setNamespaceId("f47ac10b-58cc-4372-a567-0e02b2c3d479")
.build();
var resp = sdk.getServices().namespaces()
.getNamespaceBlocking(req, Collections.emptyMap()).execute();
System.out.println("Namespace: " + resp.getNamespace().getName());
// Alternatively, look up by FQN
var reqByFqn = GetNamespaceRequest.newBuilder()
.setFqn("https://example.com")
.build();
var respByFqn = sdk.getServices().namespaces()
.getNamespaceBlocking(reqByFqn, Collections.emptyMap()).execute();
System.out.println("Namespace by FQN: " + respByFqn.getNamespace().getName());
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
// Look up by UUID
let resp = await platform.v1.namespace.getNamespace({ id: 'f47ac10b-58cc-4372-a567-0e02b2c3d479' });
console.log(`Namespace: ${resp.namespace?.name} (ID: ${resp.namespace?.id})`);
// Alternatively, look up by FQN
resp = await platform.v1.namespace.getNamespace({ fqn: 'https://example.com' });
console.log(`Namespace by FQN: ${resp.namespace?.name}`);
Update a Namespace
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/common"
"github.com/opentdf/platform/protocol/go/policy/namespaces"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.Namespaces.UpdateNamespace(context.Background(),
&namespaces.UpdateNamespaceRequest{
Id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
Metadata: &common.MetadataMutable{
Labels: map[string]string{
"owner": "platform-team",
"env": "production",
},
},
MetadataUpdateBehavior: common.MetadataUpdateEnum_METADATA_UPDATE_ENUM_REPLACE,
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Updated namespace ID: %s\n", resp.GetNamespace().GetId())
}
MetadataUpdateEnum_METADATA_UPDATE_ENUM_REPLACE overwrites all existing labels. Use METADATA_UPDATE_ENUM_EXTEND to merge with existing labels.
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.namespaces.UpdateNamespaceRequest;
import io.opentdf.platform.common.Common.MetadataMutable;
import io.opentdf.platform.common.Common.MetadataUpdateEnum;
import java.util.Collections;
public class UpdateNamespaceExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var metadata = MetadataMutable.newBuilder()
.putLabels("owner", "platform-team")
.putLabels("env", "production")
.build();
var req = UpdateNamespaceRequest.newBuilder()
.setId("f47ac10b-58cc-4372-a567-0e02b2c3d479")
.setMetadata(metadata)
.setMetadataUpdateBehavior(MetadataUpdateEnum.METADATA_UPDATE_ENUM_REPLACE)
.build();
var resp = sdk.getServices().namespaces()
.updateNamespaceBlocking(req, Collections.emptyMap()).execute();
System.out.println("Updated namespace ID: " + resp.getNamespace().getId());
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.namespace.updateNamespace({
id: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
metadata: { labels: { owner: 'platform-team', env: 'production' } },
metadataUpdateBehavior: 'METADATA_UPDATE_ENUM_REPLACE',
});
console.log(`Updated namespace ID: ${resp.namespace?.id}`);
Deactivate a Namespace
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/namespaces"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
_, err = client.Namespaces.DeactivateNamespace(context.Background(),
&namespaces.DeactivateNamespaceRequest{
Id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
},
)
if err != nil {
log.Fatal(err)
}
log.Println("Namespace deactivated.")
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.namespaces.DeactivateNamespaceRequest;
import java.util.Collections;
public class DeactivateNamespaceExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = DeactivateNamespaceRequest.newBuilder()
.setId("f47ac10b-58cc-4372-a567-0e02b2c3d479")
.build();
sdk.getServices().namespaces()
.deactivateNamespaceBlocking(req, Collections.emptyMap()).execute();
System.out.println("Namespace deactivated.");
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
await platform.v1.namespace.deactivateNamespace({ id: 'f47ac10b-58cc-4372-a567-0e02b2c3d479' });
console.log('Namespace deactivated.');
By default, ListNamespaces returns only active namespaces. To include inactive ones, set State to ACTIVE_STATE_ENUM_INACTIVE or ACTIVE_STATE_ENUM_ANY on the request.
Attributes
An attribute defines a classification dimension (e.g., classification, department). Each attribute belongs to a namespace, has a rule (ALL_OF, ANY_OF, or HIERARCHY), and contains one or more values.
Attributes and attribute values are deactivated, not permanently deleted.
Create Attribute
- Go
- Java
- TypeScript
package main
import (
"context"
"crypto/rand"
"log"
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/attributes"
"github.com/opentdf/platform/protocol/go/policy/namespaces"
"github.com/opentdf/platform/sdk"
)
func main() {
platformEndpoint := "http://localhost:8080"
// Create a new client
client, err := sdk.New(
platformEndpoint,
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
// List namespaces to get a namespace ID
listResponse, err := client.Namespaces.ListNamespaces(context.Background(), &namespaces.ListNamespacesRequest{})
if err != nil {
log.Fatalf("failed to list namespaces: %s", err)
}
if len(listResponse.GetNamespaces()) == 0 {
log.Fatal("no namespaces found")
}
namespaceID := listResponse.GetNamespaces()[0].GetId()
// Create a new attribute
attrRequest := &attributes.CreateAttributeRequest{
NamespaceId: namespaceID,
Name: "role" + "-" + rand.Text()[:4],
Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF,
Values: []string{"admin", "developer", "guest"},
}
attribute, err := client.Attributes.CreateAttribute(context.Background(), attrRequest)
if err != nil {
log.Fatal(err)
}
log.Printf("Created attribute: %s with ID: %s in namespace: %s\n", attribute.GetAttribute().Name, attribute.GetAttribute().GetId(), namespaceID)
}
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.AttributeRuleTypeEnum;
import io.opentdf.platform.policy.Namespace;
import io.opentdf.platform.policy.attributes.CreateAttributeRequest;
import io.opentdf.platform.policy.attributes.CreateAttributeResponse;
import io.opentdf.platform.policy.namespaces.GetNamespaceRequest;
import io.opentdf.platform.sdk.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.Arrays;
import java.util.Objects;
public class CreateAttribute {
private static final Logger logger = LogManager.getLogger(CreateAttribute.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
String namespaceName = "opentdf.io";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
Namespace namespace =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.namespaces()
.getNamespaceBlocking(
GetNamespaceRequest.newBuilder()
.setFqn("https://" + namespaceName)
.build(),
Collections.emptyMap())
.execute())
.getNamespace();
CreateAttributeRequest createAttributeRequest =
CreateAttributeRequest.newBuilder()
.setNamespaceId(namespace.getId())
.setName("test-attribute")
.setRule(
AttributeRuleTypeEnum.forNumber(
AttributeRuleTypeEnum.ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF_VALUE))
.addAllValues(Arrays.asList("test1", "test2"))
.build();
CreateAttributeResponse createAttributeResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.attributes()
.createAttributeBlocking(createAttributeRequest, Collections.emptyMap())
.execute());
logger.info(
"Successfully created attribute with ID: {}",
createAttributeResponse.getAttribute().getId());
} catch (Exception e) {
if (Objects.equals(e.getMessage(), "resource not found")) {
logger.error("Namespace '{}' not found", namespaceName, e);
} else if (Objects.equals(e.getMessage(), "resource unique field violation")) {
logger.error("Attribute already exists", e);
} else {
logger.error("Failed to create attribute", e);
}
}
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
import { AttributeRuleTypeEnum } from '@opentdf/sdk/platform/policy/objects_pb.js';
const platform = new PlatformClient({
authProvider,
platformUrl: 'http://localhost:8080',
});
// List namespaces to get a namespace ID
const nsResp = await platform.v1.namespace.listNamespaces({});
if (nsResp.namespaces.length === 0) {
throw new Error('no namespaces found');
}
const namespaceId = nsResp.namespaces[0].id;
// Create a new attribute
const attrResp = await platform.v1.attributes.createAttribute({
namespaceId,
name: 'role',
rule: AttributeRuleTypeEnum.ANY_OF,
values: ['admin', 'developer', 'guest'],
});
console.log(
'Created attribute:',
attrResp.attribute?.name,
'with ID:',
attrResp.attribute?.id
);
List Attributes
- Go
- Java
- TypeScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/attributes"
"github.com/opentdf/platform/sdk"
)
func main() {
platformEndpoint := "http://localhost:8080"
// Create a new client
client, err := sdk.New(
platformEndpoint,
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
// List attributes
attrs, err := client.Attributes.ListAttributes(context.Background(), &attributes.ListAttributesRequest{})
if err != nil {
log.Fatal(err)
}
for _, attr := range attrs.GetAttributes() {
log.Printf("Attribute: %s, ID: %s, ", attr.GetName(), attr.GetId())
for _, value := range attr.Values {
log.Printf("Value: %s, ID: %s", value.GetValue(), value.GetId())
}
}
}
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.Attribute;
import io.opentdf.platform.policy.attributes.ListAttributesRequest;
import io.opentdf.platform.policy.attributes.ListAttributesResponse;
import io.opentdf.platform.sdk.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class ListAttributes {
private static final Logger logger = LogManager.getLogger(ListAttributes.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
String namespaceName = "opentdf.io";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
ListAttributesRequest request =
ListAttributesRequest.newBuilder().setNamespace(namespaceName).build();
ListAttributesResponse listAttributesResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.attributes()
.listAttributesBlocking(request, Collections.emptyMap())
.execute());
List<Attribute> attributes = listAttributesResponse.getAttributesList();
logger.info(
"Successfully retrieved attributes: [{}]",
attributes.stream().map(Attribute::getFqn).collect(Collectors.joining(", ")));
} catch (Exception e) {
logger.error("Failed to list attributes", e);
}
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
const platform = new PlatformClient({
authProvider,
platformUrl: 'http://localhost:8080',
});
// List attributes
const resp = await platform.v1.attributes.listAttributes({});
for (const attr of resp.attributes) {
console.log('Attribute:', attr.name, 'ID:', attr.id);
for (const value of attr.values) {
console.log(' Value:', value.value, 'ID:', value.id);
}
}
Get an Attribute
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/attributes"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
// Look up by FQN
resp, err := client.Attributes.GetAttribute(context.Background(),
&attributes.GetAttributeRequest{
Identifier: &attributes.GetAttributeRequest_Fqn{
Fqn: "https://example.com/attr/classification",
},
},
)
if err != nil {
log.Fatal(err)
}
attr := resp.GetAttribute()
log.Printf("Attribute: %s, Rule: %s\n", attr.GetName(), attr.GetRule())
for _, v := range attr.GetValues() {
log.Printf(" Value: %s (ID: %s)\n", v.GetValue(), v.GetId())
}
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.attributes.GetAttributeRequest;
import java.util.Collections;
public class GetAttributeExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = GetAttributeRequest.newBuilder()
.setFqn("https://example.com/attr/classification")
.build();
var resp = sdk.getServices().attributes()
.getAttributeBlocking(req, Collections.emptyMap()).execute();
var attr = resp.getAttribute();
System.out.println("Attribute: " + attr.getName() + " (" + attr.getFqn() + ")");
for (var value : attr.getValuesList()) {
System.out.println(" Value: " + value.getValue() + " (ID: " + value.getId() + ")");
}
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
// Look up by FQN
const resp = await platform.v1.attributes.getAttribute({
fqn: 'https://example.com/attr/classification',
});
console.log(`Attribute: ${resp.attribute?.name}, Rule: ${resp.attribute?.rule}`);
for (const v of resp.attribute?.values ?? []) {
console.log(` Value: ${v.value} (ID: ${v.id})`);
}
Update an Attribute
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/common"
"github.com/opentdf/platform/protocol/go/policy/attributes"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.Attributes.UpdateAttribute(context.Background(),
&attributes.UpdateAttributeRequest{
Id: "a1b2c3d4-0000-0000-0000-000000000001",
Metadata: &common.MetadataMutable{
Labels: map[string]string{"reviewed": "true"},
},
MetadataUpdateBehavior: common.MetadataUpdateEnum_METADATA_UPDATE_ENUM_EXTEND,
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Updated attribute ID: %s\n", resp.GetAttribute().GetId())
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.attributes.UpdateAttributeRequest;
import io.opentdf.platform.common.Common.MetadataMutable;
import io.opentdf.platform.common.Common.MetadataUpdateEnum;
import java.util.Collections;
public class UpdateAttributeExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = UpdateAttributeRequest.newBuilder()
.setId("a1b2c3d4-0000-0000-0000-000000000001")
.setMetadata(MetadataMutable.newBuilder()
.putLabels("reviewed", "true")
.build())
.setMetadataUpdateBehavior(MetadataUpdateEnum.METADATA_UPDATE_ENUM_EXTEND)
.build();
var resp = sdk.getServices().attributes()
.updateAttributeBlocking(req, Collections.emptyMap()).execute();
System.out.println("Updated attribute ID: " + resp.getAttribute().getId());
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.attributes.updateAttribute({
id: 'a1b2c3d4-0000-0000-0000-000000000001',
metadata: { labels: { reviewed: 'true' } },
metadataUpdateBehavior: 'METADATA_UPDATE_ENUM_EXTEND',
});
console.log(`Updated attribute ID: ${resp.attribute?.id}`);
Deactivate an Attribute
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/attributes"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.Attributes.DeactivateAttribute(context.Background(),
&attributes.DeactivateAttributeRequest{
Id: "a1b2c3d4-0000-0000-0000-000000000001",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Deactivated attribute: %s\n", resp.GetAttribute().GetId())
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.attributes.DeactivateAttributeRequest;
import java.util.Collections;
public class DeactivateAttributeExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = DeactivateAttributeRequest.newBuilder()
.setId("a1b2c3d4-0000-0000-0000-000000000001")
.build();
var resp = sdk.getServices().attributes()
.deactivateAttributeBlocking(req, Collections.emptyMap()).execute();
System.out.println("Deactivated attribute: " + resp.getAttribute().getId());
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.attributes.deactivateAttribute({
id: 'a1b2c3d4-0000-0000-0000-000000000001',
});
console.log(`Deactivated attribute: ${resp.attribute?.id}`);
Attribute Values
Attribute values are the individual members of an attribute (e.g., secret, top-secret under classification). They can be managed independently after the parent attribute is created.
Create an Attribute Value
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/attributes"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.Attributes.CreateAttributeValue(context.Background(),
&attributes.CreateAttributeValueRequest{
AttributeId: "a1b2c3d4-0000-0000-0000-000000000001",
Value: "top-secret",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Created value: %s (ID: %s)\n", resp.GetValue().GetValue(), resp.GetValue().GetId())
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.attributes.CreateAttributeValueRequest;
import java.util.Collections;
public class CreateAttributeValueExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = CreateAttributeValueRequest.newBuilder()
.setAttributeId("a1b2c3d4-0000-0000-0000-000000000001")
.setValue("top-secret")
.build();
var resp = sdk.getServices().attributes()
.createAttributeValueBlocking(req, Collections.emptyMap()).execute();
System.out.println("Created value: " + resp.getValue().getValue() + " (ID: " + resp.getValue().getId() + ")");
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.attributes.createAttributeValue({
attributeId: 'a1b2c3d4-0000-0000-0000-000000000001',
value: 'top-secret',
});
console.log(`Created value: ${resp.value?.value} (ID: ${resp.value?.id})`);
List Attribute Values
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/attributes"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.Attributes.ListAttributeValues(context.Background(),
&attributes.ListAttributeValuesRequest{
AttributeId: "a1b2c3d4-0000-0000-0000-000000000001",
},
)
if err != nil {
log.Fatal(err)
}
for _, v := range resp.GetValues() {
log.Printf("Value: %s (ID: %s)\n", v.GetValue(), v.GetId())
}
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.attributes.ListAttributeValuesRequest;
import java.util.Collections;
public class ListAttributeValuesExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = ListAttributeValuesRequest.newBuilder()
.setAttributeId("a1b2c3d4-0000-0000-0000-000000000001")
.build();
var resp = sdk.getServices().attributes()
.listAttributeValuesBlocking(req, Collections.emptyMap()).execute();
for (var value : resp.getValuesList()) {
System.out.println("Value: " + value.getValue() + " (ID: " + value.getId() + ")");
}
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.attributes.listAttributeValues({
attributeId: 'a1b2c3d4-0000-0000-0000-000000000001',
});
for (const v of resp.values) {
console.log(`Value: ${v.value} (ID: ${v.id})`);
}
Get an Attribute Value
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/attributes"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
// Look up by FQN
resp, err := client.Attributes.GetAttributeValue(context.Background(),
&attributes.GetAttributeValueRequest{
Identifier: &attributes.GetAttributeValueRequest_Fqn{
Fqn: "https://example.com/attr/classification/value/secret",
},
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Value: %s (FQN: %s)\n", resp.GetValue().GetValue(), resp.GetValue().GetFqn())
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.attributes.GetAttributeValueRequest;
import java.util.Collections;
public class GetAttributeValueExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = GetAttributeValueRequest.newBuilder()
.setFqn("https://example.com/attr/classification/value/secret")
.build();
var resp = sdk.getServices().attributes()
.getAttributeValueBlocking(req, Collections.emptyMap()).execute();
System.out.println("Value: " + resp.getValue().getValue() + " (FQN: " + resp.getValue().getFqn() + ")");
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
// Look up by FQN
const resp = await platform.v1.attributes.getAttributeValue({
fqn: 'https://example.com/attr/classification/value/secret',
});
console.log(`Value: ${resp.value?.value} (FQN: ${resp.value?.fqn})`);
Get Attribute Values by FQNs
- Go
- Java
- JavaScript
Batch-fetch multiple attribute values by their FQNs in a single request. Returns a map keyed by FQN, each entry containing both the parent attribute definition and the specific value.
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/attributes"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.Attributes.GetAttributeValuesByFqns(context.Background(),
&attributes.GetAttributeValuesByFqnsRequest{
Fqns: []string{
"https://example.com/attr/classification/value/secret",
"https://example.com/attr/department/value/engineering",
},
},
)
if err != nil {
log.Fatal(err)
}
for fqn, entry := range resp.GetFqnAttributeValues() {
log.Printf("FQN: %s -> Attribute: %s, Value: %s\n",
fqn,
entry.GetAttribute().GetName(),
entry.GetValue().GetValue(),
)
}
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.attributes.GetAttributeValuesByFqnsRequest;
import java.util.Collections;
import java.util.List;
public class GetAttributeValuesByFqnsExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = GetAttributeValuesByFqnsRequest.newBuilder()
.addAllFqns(List.of(
"https://example.com/attr/classification/value/secret",
"https://example.com/attr/department/value/engineering"
))
.build();
var resp = sdk.getServices().attributes()
.getAttributeValuesByFqnsBlocking(req, Collections.emptyMap()).execute();
resp.getFqnAttributeValuesMap().forEach((fqn, entry) ->
System.out.println("FQN: " + fqn + " -> Attribute: " + entry.getAttribute().getName() + ", Value: " + entry.getValue().getValue())
);
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.attributes.getAttributeValuesByFqns({
fqns: [
'https://example.com/attr/classification/value/secret',
'https://example.com/attr/department/value/engineering',
],
});
for (const [fqn, entry] of Object.entries(resp.fqnAttributeValues)) {
console.log(`FQN: ${fqn} -> Attribute: ${entry.attribute?.name}, Value: ${entry.value?.value}`);
}
Update an Attribute Value
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/common"
"github.com/opentdf/platform/protocol/go/policy/attributes"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.Attributes.UpdateAttributeValue(context.Background(),
&attributes.UpdateAttributeValueRequest{
Id: "v1b2c3d4-0000-0000-0000-000000000001",
Metadata: &common.MetadataMutable{
Labels: map[string]string{"deprecated": "false"},
},
MetadataUpdateBehavior: common.MetadataUpdateEnum_METADATA_UPDATE_ENUM_EXTEND,
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Updated value ID: %s\n", resp.GetValue().GetId())
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.attributes.UpdateAttributeValueRequest;
import io.opentdf.platform.common.Common.MetadataMutable;
import io.opentdf.platform.common.Common.MetadataUpdateEnum;
import java.util.Collections;
public class UpdateAttributeValueExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = UpdateAttributeValueRequest.newBuilder()
.setId("v1b2c3d4-0000-0000-0000-000000000001")
.setMetadata(MetadataMutable.newBuilder()
.putLabels("deprecated", "false")
.build())
.setMetadataUpdateBehavior(MetadataUpdateEnum.METADATA_UPDATE_ENUM_EXTEND)
.build();
var resp = sdk.getServices().attributes()
.updateAttributeValueBlocking(req, Collections.emptyMap()).execute();
System.out.println("Updated value ID: " + resp.getValue().getId());
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.attributes.updateAttributeValue({
id: 'v1b2c3d4-0000-0000-0000-000000000001',
metadata: { labels: { deprecated: 'false' } },
metadataUpdateBehavior: 'METADATA_UPDATE_ENUM_EXTEND',
});
console.log(`Updated value ID: ${resp.value?.id}`);
Deactivate an Attribute Value
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/attributes"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.Attributes.DeactivateAttributeValue(context.Background(),
&attributes.DeactivateAttributeValueRequest{
Id: "v1b2c3d4-0000-0000-0000-000000000001",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Deactivated value: %s\n", resp.GetValue().GetId())
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.attributes.DeactivateAttributeValueRequest;
import java.util.Collections;
public class DeactivateAttributeValueExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = DeactivateAttributeValueRequest.newBuilder()
.setId("v1b2c3d4-0000-0000-0000-000000000001")
.build();
var resp = sdk.getServices().attributes()
.deactivateAttributeValueBlocking(req, Collections.emptyMap()).execute();
System.out.println("Deactivated value: " + resp.getValue().getId());
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.attributes.deactivateAttributeValue({
id: 'v1b2c3d4-0000-0000-0000-000000000001',
});
console.log(`Deactivated value: ${resp.value?.id}`);
Subject Condition Sets
A subject condition set (SCS) is a reusable, named set of conditions that evaluate claims from an entity's token. Multiple subject mappings can reference the same SCS.
Subject condition sets are permanently deleted (not deactivated).
Create Subject Condition Set
- Go
- Java
- TypeScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
"github.com/opentdf/platform/sdk"
)
func main() {
platformEndpoint := "http://localhost:8080"
// Create a new client
client, err := sdk.New(
platformEndpoint,
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
// Create Subject Condition Set
conditionset := &subjectmapping.CreateSubjectConditionSetRequest{
SubjectConditionSet: &subjectmapping.SubjectConditionSetCreate{
SubjectSets: []*policy.SubjectSet{
{
ConditionGroups: []*policy.ConditionGroup{
{
BooleanOperator: policy.ConditionBooleanTypeEnum_CONDITION_BOOLEAN_TYPE_ENUM_AND,
Conditions: []*policy.Condition{
{
SubjectExternalSelectorValue: ".clientId",
Operator: policy.SubjectMappingOperatorEnum_SUBJECT_MAPPING_OPERATOR_ENUM_IN,
SubjectExternalValues: []string{"opentdf"},
},
},
},
},
},
},
},
}
resp, err := client.SubjectMapping.CreateSubjectConditionSet(context.Background(), conditionset)
if err != nil {
log.Fatal(err)
}
log.Printf("Created Subject Condition Set with ID: %s\n", resp.GetSubjectConditionSet().GetId())
}
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.Condition;
import io.opentdf.platform.policy.ConditionBooleanTypeEnum;
import io.opentdf.platform.policy.ConditionGroup;
import io.opentdf.platform.policy.SubjectConditionSet;
import io.opentdf.platform.policy.SubjectMappingOperatorEnum;
import io.opentdf.platform.policy.SubjectSet;
import io.opentdf.platform.policy.subjectmapping.CreateSubjectConditionSetRequest;
import io.opentdf.platform.policy.subjectmapping.CreateSubjectConditionSetResponse;
import io.opentdf.platform.policy.subjectmapping.SubjectConditionSetCreate;
import io.opentdf.platform.sdk.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
public class CreateSubjectConditionSet {
private static final Logger logger = LogManager.getLogger(CreateSubjectConditionSet.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
SubjectSet.Builder subjectSetBuilder =
SubjectSet.newBuilder()
.addConditionGroups(
ConditionGroup.newBuilder()
.setBooleanOperator(ConditionBooleanTypeEnum.CONDITION_BOOLEAN_TYPE_ENUM_AND)
.addConditions(
Condition.newBuilder()
.setSubjectExternalSelectorValue(".myfield")
.setOperator(
SubjectMappingOperatorEnum.SUBJECT_MAPPING_OPERATOR_ENUM_IN)
.addSubjectExternalValues("myvalue")));
CreateSubjectConditionSetRequest createSubjectConditionSetRequest =
CreateSubjectConditionSetRequest.newBuilder()
.setSubjectConditionSet(
SubjectConditionSetCreate.newBuilder().addSubjectSets(subjectSetBuilder))
.build();
CreateSubjectConditionSetResponse createSubjectConditionSetResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.subjectMappings()
.createSubjectConditionSetBlocking(
createSubjectConditionSetRequest, Collections.emptyMap())
.execute());
SubjectConditionSet subjectConditionSet =
createSubjectConditionSetResponse.getSubjectConditionSet();
logger.info(
"Successfully created subject condition set with ID: {}", subjectConditionSet.getId());
} catch (Exception e) {
logger.error("Failed to create subject condition set", e);
}
}
}
import { create } from '@bufbuild/protobuf';
import { PlatformClient } from '@opentdf/sdk/platform';
import {
ConditionBooleanTypeEnum,
SubjectMappingOperatorEnum,
} from '@opentdf/sdk/platform/policy/objects_pb.js';
import {
CreateSubjectConditionSetRequestSchema,
} from '@opentdf/sdk/platform/policy/subjectmapping/subject_mapping_pb.js';
const platform = new PlatformClient({
authProvider,
platformUrl: 'http://localhost:8080',
});
// Create Subject Condition Set
// Using create() from @bufbuild/protobuf for proper type handling
const request = create(CreateSubjectConditionSetRequestSchema, {
subjectConditionSet: {
subjectSets: [
{
conditionGroups: [
{
booleanOperator: ConditionBooleanTypeEnum.AND,
conditions: [
{
subjectExternalSelectorValue: '.clientId',
operator: SubjectMappingOperatorEnum.IN,
subjectExternalValues: ['opentdf'],
},
],
},
],
},
],
},
});
const resp = await platform.v1.subjectMapping.createSubjectConditionSet(request);
console.log(
'Created Subject Condition Set with ID:',
resp.subjectConditionSet?.id
);
List Subject Condition Sets
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.SubjectMapping.ListSubjectConditionSets(context.Background(),
&subjectmapping.ListSubjectConditionSetsRequest{},
)
if err != nil {
log.Fatal(err)
}
for _, scs := range resp.GetSubjectConditionSets() {
log.Printf("SCS ID: %s\n", scs.GetId())
}
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.subjectmapping.ListSubjectConditionSetsRequest;
import java.util.Collections;
public class ListSubjectConditionSetsExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = ListSubjectConditionSetsRequest.newBuilder().build();
var resp = sdk.getServices().subjectMappings()
.listSubjectConditionSetsBlocking(req, Collections.emptyMap()).execute();
for (var scs : resp.getSubjectConditionSetsList()) {
System.out.println("SCS ID: " + scs.getId());
}
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.subjectMapping.listSubjectConditionSets({});
for (const scs of resp.subjectConditionSets) {
console.log(`SCS ID: ${scs.id}`);
}
Get a Subject Condition Set
- Go
- Java
- JavaScript
The response also includes AssociatedSubjectMappings — all subject mappings currently referencing this condition set.
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.SubjectMapping.GetSubjectConditionSet(context.Background(),
&subjectmapping.GetSubjectConditionSetRequest{
Id: "a0b1c2d3-0000-0000-0000-000000000099",
},
)
if err != nil {
log.Fatal(err)
}
scs := resp.GetSubjectConditionSet()
log.Printf("SCS ID: %s\n", scs.GetId())
log.Printf("Associated subject mappings: %d\n", len(resp.GetAssociatedSubjectMappings()))
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.subjectmapping.GetSubjectConditionSetRequest;
import java.util.Collections;
public class GetSubjectConditionSetExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = GetSubjectConditionSetRequest.newBuilder()
.setId("a0b1c2d3-0000-0000-0000-000000000099")
.build();
var resp = sdk.getServices().subjectMappings()
.getSubjectConditionSetBlocking(req, Collections.emptyMap()).execute();
System.out.println("SCS ID: " + resp.getSubjectConditionSet().getId());
System.out.println("Associated mappings: " + resp.getAssociatedSubjectMappingsList().size());
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.subjectMapping.getSubjectConditionSet({
id: 'a0b1c2d3-0000-0000-0000-000000000099',
});
console.log(`SCS ID: ${resp.subjectConditionSet?.id}`);
console.log(`Associated subject mappings: ${resp.associatedSubjectMappings.length}`);
Update a Subject Condition Set
- Go
- Java
- JavaScript
Providing SubjectSets replaces the entire condition tree. Omit it to update metadata only.
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.SubjectMapping.UpdateSubjectConditionSet(context.Background(),
&subjectmapping.UpdateSubjectConditionSetRequest{
Id: "a0b1c2d3-0000-0000-0000-000000000099",
SubjectSets: []*policy.SubjectSet{
{
ConditionGroups: []*policy.ConditionGroup{
{
BooleanOperator: policy.ConditionBooleanTypeEnum_CONDITION_BOOLEAN_TYPE_ENUM_AND,
Conditions: []*policy.Condition{
{
SubjectExternalSelectorValue: ".clientId",
Operator: policy.SubjectMappingOperatorEnum_SUBJECT_MAPPING_OPERATOR_ENUM_IN,
SubjectExternalValues: []string{"my-service", "my-other-service"},
},
},
},
},
},
},
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Updated SCS ID: %s\n", resp.GetSubjectConditionSet().GetId())
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.*;
import io.opentdf.platform.policy.subjectmapping.UpdateSubjectConditionSetRequest;
import java.util.Collections;
public class UpdateSubjectConditionSetExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var condition = Condition.newBuilder()
.setSubjectExternalSelectorValue(".clientId")
.setOperator(SubjectMappingOperatorEnum.SUBJECT_MAPPING_OPERATOR_ENUM_IN)
.addSubjectExternalValues("my-service")
.addSubjectExternalValues("my-other-service")
.build();
var conditionGroup = ConditionGroup.newBuilder()
.setBooleanOperator(ConditionBooleanTypeEnum.CONDITION_BOOLEAN_TYPE_ENUM_AND)
.addConditions(condition)
.build();
var subjectSet = SubjectSet.newBuilder()
.addConditionGroups(conditionGroup)
.build();
var req = UpdateSubjectConditionSetRequest.newBuilder()
.setId("a0b1c2d3-0000-0000-0000-000000000099")
.addSubjectSets(subjectSet)
.build();
var resp = sdk.getServices().subjectMappings()
.updateSubjectConditionSetBlocking(req, Collections.emptyMap()).execute();
System.out.println("Updated SCS ID: " + resp.getSubjectConditionSet().getId());
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.subjectMapping.updateSubjectConditionSet({
id: 'a0b1c2d3-0000-0000-0000-000000000099',
subjectSets: [
{
conditionGroups: [
{
booleanOperator: 'CONDITION_BOOLEAN_TYPE_ENUM_AND',
conditions: [
{
subjectExternalSelectorValue: '.clientId',
operator: 'SUBJECT_MAPPING_OPERATOR_ENUM_IN',
subjectExternalValues: ['my-service', 'my-other-service'],
},
],
},
],
},
],
});
console.log(`Updated SCS ID: ${resp.subjectConditionSet?.id}`);
Delete a Subject Condition Set
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.SubjectMapping.DeleteSubjectConditionSet(context.Background(),
&subjectmapping.DeleteSubjectConditionSetRequest{
Id: "a0b1c2d3-0000-0000-0000-000000000099",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Deleted SCS ID: %s\n", resp.GetSubjectConditionSet().GetId())
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.subjectmapping.DeleteSubjectConditionSetRequest;
import java.util.Collections;
public class DeleteSubjectConditionSetExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = DeleteSubjectConditionSetRequest.newBuilder()
.setId("a0b1c2d3-0000-0000-0000-000000000099")
.build();
var resp = sdk.getServices().subjectMappings()
.deleteSubjectConditionSetBlocking(req, Collections.emptyMap()).execute();
System.out.println("Deleted SCS ID: " + resp.getSubjectConditionSet().getId());
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.subjectMapping.deleteSubjectConditionSet({
id: 'a0b1c2d3-0000-0000-0000-000000000099',
});
console.log(`Deleted SCS ID: ${resp.subjectConditionSet?.id}`);
Delete All Unmapped Subject Condition Sets
- Go
- Java
- JavaScript
Permanently deletes all subject condition sets that are not referenced by any subject mapping. Use this as a maintenance operation to clean up orphaned condition sets.
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.SubjectMapping.DeleteAllUnmappedSubjectConditionSets(context.Background(),
&subjectmapping.DeleteAllUnmappedSubjectConditionSetsRequest{},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Deleted %d unmapped subject condition sets.\n", len(resp.GetSubjectConditionSets()))
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.subjectmapping.DeleteAllUnmappedSubjectConditionSetsRequest;
import java.util.Collections;
public class DeleteAllUnmappedSubjectConditionSetsExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = DeleteAllUnmappedSubjectConditionSetsRequest.newBuilder().build();
var resp = sdk.getServices().subjectMappings()
.deleteAllUnmappedSubjectConditionSetsBlocking(req, Collections.emptyMap()).execute();
System.out.println("Deleted " + resp.getSubjectConditionSetsList().size() + " unmapped subject condition sets.");
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.subjectMapping.deleteAllUnmappedSubjectConditionSets({});
console.log(`Deleted ${resp.subjectConditionSets.length} unmapped subject condition sets.`);
Subject Mappings
A subject mapping links a subject condition set to a specific attribute value and a set of permitted actions (e.g., DECRYPT). It answers the question: "which entities are allowed to do what with data carrying this attribute value?"
Subject mappings are permanently deleted (not deactivated). There is no restore operation.
Create Subject Mapping
- Go
- Java
- TypeScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
"github.com/opentdf/platform/sdk"
)
func main() {
platformEndpoint := "http://localhost:8080"
// Create a new client
client, err := sdk.New(
platformEndpoint,
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
// Create Subject Mapping
subjectMapping := &subjectmapping.CreateSubjectMappingRequest{
AttributeValueId: "224c9d29-2cd4-4a38-b6ad-5f025ca93a8c",
Actions: []*policy.Action{
{
Value: &policy.Action_Standard{
Standard: policy.Action_STANDARD_ACTION_DECRYPT,
},
},
},
ExistingSubjectConditionSetId: "890b26db-4ee4-447f-ae8a-2862d922eeef",
}
_, err = client.SubjectMapping.CreateSubjectMapping(context.Background(), subjectMapping)
if err != nil {
log.Fatal(err)
}
}
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.Action;
import io.opentdf.platform.policy.Attribute;
import io.opentdf.platform.policy.Condition;
import io.opentdf.platform.policy.ConditionBooleanTypeEnum;
import io.opentdf.platform.policy.ConditionGroup;
import io.opentdf.platform.policy.Namespace;
import io.opentdf.platform.policy.SubjectConditionSet;
import io.opentdf.platform.policy.SubjectMapping;
import io.opentdf.platform.policy.SubjectMappingOperatorEnum;
import io.opentdf.platform.policy.SubjectSet;
import io.opentdf.platform.policy.attributes.GetAttributeRequest;
import io.opentdf.platform.policy.namespaces.GetNamespaceRequest;
import io.opentdf.platform.policy.subjectmapping.CreateSubjectConditionSetRequest;
import io.opentdf.platform.policy.subjectmapping.CreateSubjectConditionSetResponse;
import io.opentdf.platform.policy.subjectmapping.CreateSubjectMappingRequest;
import io.opentdf.platform.policy.subjectmapping.CreateSubjectMappingResponse;
import io.opentdf.platform.policy.subjectmapping.SubjectConditionSetCreate;
import io.opentdf.platform.sdk.SDK;
import io.opentdf.platform.sdk.SDKBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.Objects;
public class CreateSubjectMapping {
private static final Logger logger = LogManager.getLogger(CreateSubjectMapping.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
String namespaceName = "opentdf.io";
String attributeName = "test-attribute";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
Namespace namespace;
try {
namespace =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.namespaces()
.getNamespaceBlocking(
GetNamespaceRequest.newBuilder()
.setFqn("https://" + namespaceName)
.build(),
Collections.emptyMap())
.execute())
.getNamespace();
} catch (Exception e) {
if (Objects.equals(e.getMessage(), "resource not found")) {
logger.error("Namespace '{}' not found", namespaceName, e);
} else {
logger.error("Failed to retrieve namespace '{}'", namespaceName, e);
}
return;
}
Attribute attribute;
String attributeFqn = namespace.getFqn() + "/attr/" + attributeName;
try {
GetAttributeRequest getAttributeRequest =
GetAttributeRequest.newBuilder().setFqn(attributeFqn).build();
attribute =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.attributes()
.getAttributeBlocking(getAttributeRequest, Collections.emptyMap())
.execute())
.getAttribute();
} catch (Exception e) {
if (Objects.equals(e.getMessage(), "resource not found")) {
logger.error("Attribute '{}' not found", attributeFqn, e);
} else {
logger.error("Failed to retrieve attribute '{}'", attributeFqn, e);
}
return;
}
CreateSubjectConditionSetRequest subjectConditionSetRequest =
CreateSubjectConditionSetRequest.newBuilder()
.setSubjectConditionSet(
SubjectConditionSetCreate.newBuilder()
.addSubjectSets(
SubjectSet.newBuilder()
.addConditionGroups(
ConditionGroup.newBuilder()
.setBooleanOperator(
ConditionBooleanTypeEnum.CONDITION_BOOLEAN_TYPE_ENUM_AND)
.addConditions(
Condition.newBuilder()
.setSubjectExternalSelectorValue(".myfield")
.setOperator(
SubjectMappingOperatorEnum
.SUBJECT_MAPPING_OPERATOR_ENUM_IN)
.addSubjectExternalValues("myvalue")))))
.build();
CreateSubjectConditionSetResponse subjectConditionSetResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.subjectMappings()
.createSubjectConditionSetBlocking(
subjectConditionSetRequest, Collections.emptyMap())
.execute());
SubjectConditionSet subjectConditionSet =
subjectConditionSetResponse.getSubjectConditionSet();
CreateSubjectMappingRequest request =
CreateSubjectMappingRequest.newBuilder()
.setAttributeValueId(attribute.getValues(0).getId())
.addActions(Action.newBuilder().setName("read"))
.setExistingSubjectConditionSetId(subjectConditionSet.getId())
.build();
CreateSubjectMappingResponse resp =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.subjectMappings()
.createSubjectMappingBlocking(request, Collections.emptyMap())
.execute());
SubjectMapping subjectMapping = resp.getSubjectMapping();
logger.info("Successfully created subject mapping with ID: {}", subjectMapping.getId());
} catch (Exception e) {
logger.error("Failed to create subject mapping", e);
}
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
const platform = new PlatformClient({
authProvider,
platformUrl: 'http://localhost:8080',
});
// Create Subject Mapping
await platform.v1.subjectMapping.createSubjectMapping({
attributeValueId: '224c9d29-2cd4-4a38-b6ad-5f025ca93a8c',
actions: [{ name: 'decrypt' }],
existingSubjectConditionSetId: '890b26db-4ee4-447f-ae8a-2862d922eeef',
});
List Subject Mappings
- Go
- Java
- TypeScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
"github.com/opentdf/platform/sdk"
)
func main() {
platformEndpoint := "http://localhost:8080"
// Create a new client
client, err := sdk.New(
platformEndpoint,
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
// List Subject Mapping
subjectmappings, err := client.SubjectMapping.ListSubjectMappings(context.Background(), &subjectmapping.ListSubjectMappingsRequest{})
if err != nil {
log.Fatal(err)
}
for _, sm := range subjectmappings.GetSubjectMappings() {
log.Printf("Subject Mapping: %s", sm.GetId())
}
}
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.SubjectMapping;
import io.opentdf.platform.policy.subjectmapping.ListSubjectMappingsRequest;
import io.opentdf.platform.policy.subjectmapping.ListSubjectMappingsResponse;
import io.opentdf.platform.sdk.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class ListSubjectMappings {
private static final Logger logger = LogManager.getLogger(ListSubjectMappings.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
ListSubjectMappingsRequest listSubjectMappingsRequest =
ListSubjectMappingsRequest.newBuilder().build();
ListSubjectMappingsResponse listSubjectMappingsResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.subjectMappings()
.listSubjectMappingsBlocking(listSubjectMappingsRequest, Collections.emptyMap())
.execute());
List<SubjectMapping> subjectMappings = listSubjectMappingsResponse.getSubjectMappingsList();
logger.info(
"Successfully retrieved subject mappings: [{}]",
subjectMappings.stream().map(SubjectMapping::getId).collect(Collectors.joining(", ")));
} catch (Exception e) {
logger.error("Failed to list subject mappings", e);
}
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
const platform = new PlatformClient({
authProvider,
platformUrl: 'http://localhost:8080',
});
// List Subject Mappings
const resp = await platform.v1.subjectMapping.listSubjectMappings({});
for (const sm of resp.subjectMappings) {
console.log('Subject Mapping:', sm.id);
}
Get a Subject Mapping
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.SubjectMapping.GetSubjectMapping(context.Background(),
&subjectmapping.GetSubjectMappingRequest{
Id: "890b26db-4ee4-447f-ae8a-2862d922eeef",
},
)
if err != nil {
log.Fatal(err)
}
sm := resp.GetSubjectMapping()
log.Printf("Subject Mapping ID: %s, Attribute Value ID: %s\n",
sm.GetId(), sm.GetAttributeValue().GetId())
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.subjectmapping.GetSubjectMappingRequest;
import java.util.Collections;
public class GetSubjectMappingExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = GetSubjectMappingRequest.newBuilder()
.setId("890b26db-4ee4-447f-ae8a-2862d922eeef")
.build();
var resp = sdk.getServices().subjectMappings()
.getSubjectMappingBlocking(req, Collections.emptyMap()).execute();
System.out.println("Subject Mapping ID: " + resp.getSubjectMapping().getId());
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.subjectMapping.getSubjectMapping({
id: '890b26db-4ee4-447f-ae8a-2862d922eeef',
});
console.log(`Subject Mapping ID: ${resp.subjectMapping?.id}`);
Update a Subject Mapping
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
// Replace the actions list on an existing subject mapping
resp, err := client.SubjectMapping.UpdateSubjectMapping(context.Background(),
&subjectmapping.UpdateSubjectMappingRequest{
Id: "890b26db-4ee4-447f-ae8a-2862d922eeef",
Actions: []*policy.Action{
{
Value: &policy.Action_Standard{
Standard: policy.Action_STANDARD_ACTION_DECRYPT,
},
},
},
// Optionally replace the subject condition set:
// SubjectConditionSetId: "new-scs-uuid",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Updated subject mapping ID: %s\n", resp.GetSubjectMapping().GetId())
}
Providing Actions replaces the entire actions list. Omit it to leave actions unchanged. Providing SubjectConditionSetId swaps the linked condition set.
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.Action;
import io.opentdf.platform.policy.subjectmapping.UpdateSubjectMappingRequest;
import java.util.Collections;
public class UpdateSubjectMappingExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var decryptAction = Action.newBuilder()
.setStandard(Action.StandardAction.STANDARD_ACTION_DECRYPT)
.build();
var req = UpdateSubjectMappingRequest.newBuilder()
.setId("890b26db-4ee4-447f-ae8a-2862d922eeef")
.addActions(decryptAction)
.build();
var resp = sdk.getServices().subjectMappings()
.updateSubjectMappingBlocking(req, Collections.emptyMap()).execute();
System.out.println("Updated subject mapping ID: " + resp.getSubjectMapping().getId());
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.subjectMapping.updateSubjectMapping({
id: '890b26db-4ee4-447f-ae8a-2862d922eeef',
actions: [{ standard: 'STANDARD_ACTION_DECRYPT' }],
});
console.log(`Updated subject mapping ID: ${resp.subjectMapping?.id}`);
Delete a Subject Mapping
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.SubjectMapping.DeleteSubjectMapping(context.Background(),
&subjectmapping.DeleteSubjectMappingRequest{
Id: "890b26db-4ee4-447f-ae8a-2862d922eeef",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Deleted subject mapping ID: %s\n", resp.GetSubjectMapping().GetId())
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.subjectmapping.DeleteSubjectMappingRequest;
import java.util.Collections;
public class DeleteSubjectMappingExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = DeleteSubjectMappingRequest.newBuilder()
.setId("890b26db-4ee4-447f-ae8a-2862d922eeef")
.build();
var resp = sdk.getServices().subjectMappings()
.deleteSubjectMappingBlocking(req, Collections.emptyMap()).execute();
System.out.println("Deleted subject mapping ID: " + resp.getSubjectMapping().getId());
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.subjectMapping.deleteSubjectMapping({
id: '890b26db-4ee4-447f-ae8a-2862d922eeef',
});
console.log(`Deleted subject mapping ID: ${resp.subjectMapping?.id}`);
Match Subject Mappings
- Go
- Java
- JavaScript
Given a list of subject properties (key-value pairs from an entity's claims or attributes), return all subject mappings whose condition sets match. This is used to determine which access grants apply to a given entity.
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.SubjectMapping.MatchSubjectMappings(context.Background(),
&subjectmapping.MatchSubjectMappingsRequest{
SubjectProperties: []*policy.SubjectProperty{
{ExternalSelectorValue: ".clientId", ExternalValue: "my-service"},
{ExternalSelectorValue: ".realm_access.roles", ExternalValue: "developer"},
},
},
)
if err != nil {
log.Fatal(err)
}
for _, sm := range resp.GetSubjectMappings() {
log.Printf("Matched subject mapping ID: %s\n", sm.GetId())
}
}
package io.opentdf.platform;
import io.opentdf.platform.sdk.*;
import io.opentdf.platform.policy.SubjectProperty;
import io.opentdf.platform.policy.subjectmapping.MatchSubjectMappingsRequest;
import java.util.Collections;
public class MatchSubjectMappingsExample {
public static void main(String[] args) throws Exception {
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
var req = MatchSubjectMappingsRequest.newBuilder()
.addSubjectProperties(SubjectProperty.newBuilder()
.setExternalSelectorValue(".clientId")
.setExternalValue("my-service")
.build())
.addSubjectProperties(SubjectProperty.newBuilder()
.setExternalSelectorValue(".realm_access.roles")
.setExternalValue("developer")
.build())
.build();
var resp = sdk.getServices().subjectMappings()
.matchSubjectMappingsBlocking(req, Collections.emptyMap()).execute();
for (var sm : resp.getSubjectMappingsList()) {
System.out.println("Matched subject mapping ID: " + sm.getId());
}
sdk.close();
}
}
import { PlatformClient } from '@opentdf/sdk/platform';
// See /sdks/platform-client for full setup including DPoP key binding.
const platform = new PlatformClient({ authProvider, platformUrl: 'http://localhost:8080' });
const resp = await platform.v1.subjectMapping.matchSubjectMappings({
subjectProperties: [
{ externalSelectorValue: '.clientId', externalValue: 'my-service' },
{ externalSelectorValue: '.realm_access.roles', externalValue: 'developer' },
],
});
for (const sm of resp.subjectMappings) {
console.log(`Matched subject mapping ID: ${sm.id}`);
}
Obligations
An obligation is a PDP-to-PEP directive that accompanies an access decision: "permit, provided these controls are enforced." Obligations are scoped to a namespace and can carry multiple values and triggers. See Obligations for the policy concept.
Access to the Obligations service:
- Go
- JavaScript
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("client-id", "client-secret", nil),
)
if err != nil {
log.Fatal(err)
}
// sdk.Obligations provides the full ObligationsServiceClient interface.
import { PlatformClient } from '@opentdf/sdk/platform';
const platform = new PlatformClient({
authProvider,
platformUrl: 'http://localhost:8080',
});
// platform.v1.obligation provides the full Obligations service interface.
Obligation Definitions
List Obligations
- Go
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/obligations"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.Obligations.ListObligations(context.Background(),
&obligations.ListObligationsRequest{},
)
if err != nil {
log.Fatal(err)
}
for _, obl := range resp.GetObligations() {
log.Printf("Obligation: %s FQN: %s\n", obl.GetName(), obl.GetFqn())
}
}
To filter by namespace, set NamespaceId or NamespaceFqn on the request:
resp, err := client.Obligations.ListObligations(context.Background(),
&obligations.ListObligationsRequest{
NamespaceFqn: "https://example.com",
},
)
const resp = await platform.v1.obligation.listObligations({});
for (const obl of resp.obligations) {
console.log(`Obligation: ${obl.name} FQN: ${obl.fqn}`);
}
To filter by namespace:
const resp = await platform.v1.obligation.listObligations({
namespaceFqn: 'https://example.com',
});
Get an Obligation
Look up by ID or FQN (one is required):
- Go
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/obligations"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.Obligations.GetObligation(context.Background(),
&obligations.GetObligationRequest{
Fqn: "https://example.com/obl/drm",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Obligation ID: %s\n", resp.GetObligation().GetId())
}
const resp = await platform.v1.obligation.getObligation({
fqn: 'https://example.com/obl/drm',
});
console.log(`Obligation ID: ${resp.obligation?.id}`);
Get Obligations by FQNs
Batch-fetch multiple obligations by FQN. Returns a map of FQN to Obligation:
- Go
- JavaScript
resp, err := client.Obligations.GetObligationsByFQNs(context.Background(),
&obligations.GetObligationsByFQNsRequest{
Fqns: []string{
"https://example.com/obl/drm",
"https://example.com/obl/audit",
},
},
)
if err != nil {
log.Fatal(err)
}
for fqn, obl := range resp.GetFqnObligationMap() {
log.Printf("%s → %s\n", fqn, obl.GetId())
}
const resp = await platform.v1.obligation.getObligationsByFQNs({
fqns: [
'https://example.com/obl/drm',
'https://example.com/obl/audit',
],
});
for (const [fqn, obl] of Object.entries(resp.fqnObligationMap)) {
console.log(`${fqn} → ${obl.id}`);
}
Create an Obligation
Provide either a NamespaceId or NamespaceFqn (one is required), plus a Name. Values can be created inline or added later via Create Obligation Value:
- Go
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/obligations"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.Obligations.CreateObligation(context.Background(),
&obligations.CreateObligationRequest{
NamespaceFqn: "https://example.com",
Name: "drm",
Values: []string{"watermarking", "no-download"},
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Created obligation: %s FQN: %s\n",
resp.GetObligation().GetId(), resp.GetObligation().GetFqn())
}
const resp = await platform.v1.obligation.createObligation({
namespaceFqn: 'https://example.com',
name: 'drm',
values: ['watermarking', 'no-download'],
});
console.log(`Created obligation: ${resp.obligation?.id} FQN: ${resp.obligation?.fqn}`);
The resulting FQN follows the convention <namespace>/obl/<name> (e.g. https://example.com/obl/drm).
Update an Obligation
Only the fields you set are updated. Provide the obligation Id:
- Go
- JavaScript
resp, err := client.Obligations.UpdateObligation(context.Background(),
&obligations.UpdateObligationRequest{
Id: "3f4a7c12-...",
Name: "digital-rights",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Updated obligation FQN: %s\n", resp.GetObligation().GetFqn())
const resp = await platform.v1.obligation.updateObligation({
id: '3f4a7c12-...',
name: 'digital-rights',
});
console.log(`Updated obligation FQN: ${resp.obligation?.fqn}`);
Delete an Obligation
Provide either Id or Fqn:
- Go
- JavaScript
resp, err := client.Obligations.DeleteObligation(context.Background(),
&obligations.DeleteObligationRequest{
Fqn: "https://example.com/obl/drm",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Deleted obligation: %s\n", resp.GetObligation().GetId())
const resp = await platform.v1.obligation.deleteObligation({
fqn: 'https://example.com/obl/drm',
});
console.log(`Deleted obligation: ${resp.obligation?.id}`);
Obligation Values
Each obligation can carry one or more values. Value FQNs follow the convention <namespace>/obl/<obligation_name>/value/<value> (e.g. https://example.com/obl/drm/value/watermarking).
Get an Obligation Value
- Go
- JavaScript
resp, err := client.Obligations.GetObligationValue(context.Background(),
&obligations.GetObligationValueRequest{
Fqn: "https://example.com/obl/drm/value/watermarking",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Value ID: %s\n", resp.GetValue().GetId())
const resp = await platform.v1.obligation.getObligationValue({
fqn: 'https://example.com/obl/drm/value/watermarking',
});
console.log(`Value ID: ${resp.value?.id}`);
Get Obligation Values by FQNs
- Go
- JavaScript
resp, err := client.Obligations.GetObligationValuesByFQNs(context.Background(),
&obligations.GetObligationValuesByFQNsRequest{
Fqns: []string{
"https://example.com/obl/drm/value/watermarking",
"https://example.com/obl/drm/value/no-download",
},
},
)
if err != nil {
log.Fatal(err)
}
for fqn, val := range resp.GetFqnValueMap() {
log.Printf("%s → %s\n", fqn, val.GetId())
}
const resp = await platform.v1.obligation.getObligationValuesByFQNs({
fqns: [
'https://example.com/obl/drm/value/watermarking',
'https://example.com/obl/drm/value/no-download',
],
});
for (const [fqn, val] of Object.entries(resp.fqnValueMap)) {
console.log(`${fqn} → ${val.id}`);
}
Create an Obligation Value
Provide either ObligationId or ObligationFqn, plus the Value string:
- Go
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/policy/obligations"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.Obligations.CreateObligationValue(context.Background(),
&obligations.CreateObligationValueRequest{
ObligationFqn: "https://example.com/obl/drm",
Value: "encrypt-at-rest",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Created value: %s FQN: %s\n",
resp.GetValue().GetId(), resp.GetValue().GetFqn())
}
const resp = await platform.v1.obligation.createObligationValue({
obligationFqn: 'https://example.com/obl/drm',
value: 'encrypt-at-rest',
});
console.log(`Created value: ${resp.value?.id} FQN: ${resp.value?.fqn}`);
Update an Obligation Value
Provide the value Id. Triggers provided here replace all existing triggers for the value:
- Go
- JavaScript
resp, err := client.Obligations.UpdateObligationValue(context.Background(),
&obligations.UpdateObligationValueRequest{
Id: "9a1b2c3d-...",
Value: "encrypt-at-rest-v2",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Updated value FQN: %s\n", resp.GetValue().GetFqn())
const resp = await platform.v1.obligation.updateObligationValue({
id: '9a1b2c3d-...',
value: 'encrypt-at-rest-v2',
});
console.log(`Updated value FQN: ${resp.value?.fqn}`);
Delete an Obligation Value
- Go
- JavaScript
resp, err := client.Obligations.DeleteObligationValue(context.Background(),
&obligations.DeleteObligationValueRequest{
Fqn: "https://example.com/obl/drm/value/watermarking",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Deleted value: %s\n", resp.GetValue().GetId())
const resp = await platform.v1.obligation.deleteObligationValue({
fqn: 'https://example.com/obl/drm/value/watermarking',
});
console.log(`Deleted value: ${resp.value?.id}`);
Triggers
A trigger links an obligation value to a specific action + attribute value combination (and optionally a PEP identifier). When that action is performed on data carrying that attribute value, the obligation fires.
Add an Obligation Trigger
- Go
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/protocol/go/common"
"github.com/opentdf/platform/protocol/go/policy/obligations"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
resp, err := client.Obligations.AddObligationTrigger(context.Background(),
&obligations.AddObligationTriggerRequest{
ObligationValue: &common.IdFqnIdentifier{
Fqn: "https://example.com/obl/drm/value/watermarking",
},
Action: &common.IdNameIdentifier{
Name: "DECRYPT",
},
AttributeValue: &common.IdFqnIdentifier{
Fqn: "https://example.com/attr/classification/value/secret",
},
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Added trigger: %s\n", resp.GetTrigger().GetId())
}
const resp = await platform.v1.obligation.addObligationTrigger({
obligationValue: { fqn: 'https://example.com/obl/drm/value/watermarking' },
action: { name: 'DECRYPT' },
attributeValue: { fqn: 'https://example.com/attr/classification/value/secret' },
});
console.log(`Added trigger: ${resp.trigger?.id}`);
List Obligation Triggers
- Go
- JavaScript
resp, err := client.Obligations.ListObligationTriggers(context.Background(),
&obligations.ListObligationTriggersRequest{
NamespaceFqn: "https://example.com",
},
)
if err != nil {
log.Fatal(err)
}
for _, trigger := range resp.GetTriggers() {
log.Printf("Trigger ID: %s\n", trigger.GetId())
}
const resp = await platform.v1.obligation.listObligationTriggers({
namespaceFqn: 'https://example.com',
});
for (const trigger of resp.triggers) {
console.log(`Trigger ID: ${trigger.id}`);
}
Remove an Obligation Trigger
- Go
- JavaScript
resp, err := client.Obligations.RemoveObligationTrigger(context.Background(),
&obligations.RemoveObligationTriggerRequest{
Id: "7e8f9a0b-...",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Removed trigger: %s\n", resp.GetTrigger().GetId())
const resp = await platform.v1.obligation.removeObligationTrigger({
id: '7e8f9a0b-...',
});
console.log(`Removed trigger: ${resp.trigger?.id}`);