Skip to content

Object tags

Object tags are a small set of key/value labels attached to an object. Use them to mark lifecycle state (status=final), ownership (team=finance), or anything you want to filter or report on later. Tags are a replace-on-write set: writing tags replaces the whole set, it does not merge into the existing one.

The tag set is independent of user metadata. Metadata is fixed at write time and travels with the object body. Tags can change after the object exists without rewriting the object.

Setting tags replaces the entire set. To add one tag, read the current set, modify it, then write it back; a

bare set call drops every existing tag. :::

Limits and format

Tags follow the S3 tagging rules:

  • Up to 10 tags per object.
  • A key is up to 128 characters; a value is up to 256 characters.
  • Keys and values are UTF-8 strings.
  • Keys are unique within the set (a repeated key keeps the last value).

The server validates the set on write and rejects an oversized or malformed set. The native path applies the same limits as the S3 path.

Get, Set, Delete

The native client uses getObjectTags, setObjectTags, and deleteObjectTags. setObjectTags replaces the whole set and returns the stored set so you can confirm what landed. The Node native client takes and returns a plain object; the Go and Java native clients use a list of Tag records ({key, value}).

ts
await native.setObjectTags("reports", "q1/summary.txt", {
  team: "finance",
  status: "final",
});

const tags = await native.getObjectTags("reports", "q1/summary.txt");
console.log(tags); // { team: 'finance', status: 'final' }

await native.deleteObjectTags("reports", "q1/summary.txt");
go
_, err := native.SetObjectTags(ctx, "reports", "q1/summary.txt", []lockwellnative.Tag{
    {Key: "team", Value: "finance"},
    {Key: "status", Value: "final"},
})

tags, err := native.GetObjectTags(ctx, "reports", "q1/summary.txt")
for _, t := range tags {
    fmt.Println(t.Key, t.Value)
}

err = native.DeleteObjectTags(ctx, "reports", "q1/summary.txt")
java
import com.lockwell.sdk.nativeapi.NativeTypes.Tag;
import java.util.List;

native.setObjectTags("reports", "q1/summary.txt",
    List.of(new Tag("team", "finance"), new Tag("status", "final")));

List<Tag> tags = native.getObjectTags("reports", "q1/summary.txt");

native.deleteObjectTags("reports", "q1/summary.txt");

::::

The S3 client exposes the same three operations under the S3 names: PutObjectTagging replaces the set, GetObjectTagging reads it, and DeleteObjectTagging clears it.

ts
await s3.putObjectTagging("reports", "q1/summary.txt", {
  team: "finance",
  status: "final",
});

const tags = await s3.getObjectTagging("reports", "q1/summary.txt");
console.log(tags.team, tags.status); // "finance" "final"

await s3.deleteObjectTagging("reports", "q1/summary.txt");
go
err := s3.PutObjectTagging(ctx, "reports", "q1/summary.txt", map[string]string{
    "team":   "finance",
    "status": "final",
})

tags, err := s3.GetObjectTagging(ctx, "reports", "q1/summary.txt")
fmt.Println(tags["team"], tags["status"]) // "finance" "final"

err = s3.DeleteObjectTagging(ctx, "reports", "q1/summary.txt")
java
import java.util.Map;

s3.putObjectTagging("reports", "q1/summary.txt",
    Map.of("team", "finance", "status", "final"));

Map<String, String> tags = s3.getObjectTagging("reports", "q1/summary.txt");
System.out.println(tags.get("team") + " " + tags.get("status"));

s3.deleteObjectTagging("reports", "q1/summary.txt");

Both clients always replace the whole set. To add one tag to an existing set, read the set, modify it, and write it back.

ts
const current = await native.getObjectTags("reports", "q1/summary.txt");
await native.setObjectTags("reports", "q1/summary.txt", { ...current, reviewed: "yes" });

Tagging a specific version

In a versioned bucket, tags belong to a version. The Node native client takes an optional versionId on its tagging methods; without it, the current version is tagged. The Go and Java native tagging methods operate on the current version only, so to read or replace the tags of a specific version from those languages, use the S3 client's WithVersionID (shown in the S3 block below).

ts
// Read the tags of a specific version:
const tags = await native.getObjectTags("reports", "q1/summary.txt", { versionId });

// Replace the tags on a specific version:
await native.setObjectTags("reports", "q1/summary.txt", { status: "archived" }, { versionId });
go
// The native tagging methods operate on the current version; reach for a versioned
// tag set with the S3 client's WithVersionID, shown in the S3 block below.
tags, err := native.GetObjectTags(ctx, "reports", "q1/summary.txt")
java
List<Tag> tags = native.getObjectTags("reports", "q1/summary.txt");

The Go S3 client's tagging methods accept WithVersionID to target a non-current version:

go
// Read the tags of a specific version:
tags, err := s3.GetObjectTagging(ctx, "reports", "q1/summary.txt",
    lockwellsdk.WithVersionID(versionID))

// Replace the tags on a specific version:
err = s3.PutObjectTagging(ctx, "reports", "q1/summary.txt",
    map[string]string{"status": "archived"},
    lockwellsdk.WithVersionID(versionID))

The Node native client (shown above) is the cleanest way to tag a specific older version from a non-Go language, since the Node and Java S3 tagging methods operate on the current version only. See versioning for how versions and version ids work.

Released under the Apache-2.0 License. License