Getting Started

@skyware/labeler is a lightweight library for operating a labeler.

Before you start, you will need:

  • An existing Bluesky account that will act as the labeler
  • A domain
  • An SSL certificate for your domain

Setup

If you already have a labeler set up, via Ozone or otherwise, you can skip this step.

To initialize the Bluesky account as a labeler, run the following command:

npx @skyware/labeler setup

The command will walk you through logging in, then ask for the URL where your labeler server will be located. This must be an HTTPS URL. A confirmation code will then be sent to your email. Once you’ve entered the code, a signing key will be generated if you did not provide one. Be sure to save this key, as you will need it to operate the labeler.

Once complete, you’ll have the option to begin defining labels that your labeler will apply. You can also do this later by running npx @skyware/labeler label add. For each label, you must define the following information:

? Identifier (non-user-facing, must be unique, 100 characters max): » A unique identifier for the label, used internally.
? Name (user-facing, 64 characters max): » The name that users will see for the label.
? Description (user-facing): » A description of the label.
? Does the user need to have adult content enabled to configure this label? » (y/N)
? Label severity:
    Informational - Displays an information symbol next to the label
    Alert - Displays a warning symbol next to the label
    None - The label will not be displayed on content
? Should this label hide content?
    Content - All content, text or media, will be hidden if labeled
    Media - Only media will be hidden if labeled
    None - The label will not hide content
? What should the default setting be for a new subscriber?
    Ignore - The label will not be displayed by default
    Warn - The label will be displayed on content, but will not hide it from feeds
    Hide - The label will be displayed on content, and labeled content will be hidden from feeds

Running the Labeler

import { class LabelerServerLabelerServer } from "@skyware/labeler";

const const server: LabelerServerserver = new new LabelerServer(options: LabelerOptions): LabelerServer
Create a labeler server.
@paramoptions Configuration options.
LabelerServer
({
LabelerOptions.did: string
The DID of the labeler account.
did
: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnv
The process.env property returns an object containing the user environment. See `environ(7)`. An example of this object looks like: { TERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node' } It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other Worker threads. In other words, the following example would not work: $ node -e 'process.env.foo = "bar"' && echo $foo While the following will: import { env } from 'process'; env.foo = 'bar'; console.log(env.foo); Assigning a property on process.env will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean. import { env } from 'process'; env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' Use delete to delete a property from process.env. import { env } from 'process'; env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined On Windows operating systems, environment variables are case-insensitive. import { env } from 'process'; env.TEST = 1; console.log(env.test); // => 1 Unless explicitly specified when creating a Worker instance, each Worker thread has its own copy of process.env, based on its parent thread’s process.env, or whatever was specified as the env option to the Worker constructor. Changes to process.env will not be visible across Worker threads, and only the main thread can make changes that are visible to the operating system or to native add-ons.
@sincev0.1.27
env
.stringLABELER_DID,
LabelerOptions.signingKey: string
The private signing key used for the labeler. If you don't have a key, generate and set one using {@link plcSetupLabeler } .
signingKey
: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnv
The process.env property returns an object containing the user environment. See `environ(7)`. An example of this object looks like: { TERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node' } It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other Worker threads. In other words, the following example would not work: $ node -e 'process.env.foo = "bar"' && echo $foo While the following will: import { env } from 'process'; env.foo = 'bar'; console.log(env.foo); Assigning a property on process.env will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean. import { env } from 'process'; env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' Use delete to delete a property from process.env. import { env } from 'process'; env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined On Windows operating systems, environment variables are case-insensitive. import { env } from 'process'; env.TEST = 1; console.log(env.test); // => 1 Unless explicitly specified when creating a Worker instance, each Worker thread has its own copy of process.env, based on its parent thread’s process.env, or whatever was specified as the env option to the Worker constructor. Changes to process.env will not be visible across Worker threads, and only the main thread can make changes that are visible to the operating system or to native add-ons.
@sincev0.1.27
env
.stringSIGNING_KEY
}); const server: LabelerServerserver.LabelerServer.start(port: number, callback?: (error: Error | null, address: string) => void): void
Start the server.
@paramport The port to listen on.@paramcallback A callback to run when the server is started.
start
(14831, (error: Error | nullerror) => {
if (error: Error | nullerror) { var console: Console
The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A Console class with methods such as console.log(), console.error() andconsole.warn() that can be used to write to any Node.js stream. * A global console instance configured to write to process.stdout and process.stderr. The global console can be used without callingrequire('console'). _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information. Example using the global console: console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr Example using the Console class: const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err
@seesource
console
.Console.error(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stderr with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to `printf(3)` (the arguments are all passed to util.format()). const code = 5; console.error('error #%d', code); // Prints: error #5, to stderr console.error('error', code); // Prints: error 5, to stderr If formatting elements (e.g. %d) are not found in the first string then util.inspect() is called on each argument and the resulting string values are concatenated. See util.format() for more information.
@sincev0.1.100
error
("Failed to start server:", error: Errorerror);
} else { var console: Console
The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A Console class with methods such as console.log(), console.error() andconsole.warn() that can be used to write to any Node.js stream. * A global console instance configured to write to process.stdout and process.stderr. The global console can be used without callingrequire('console'). _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information. Example using the global console: console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr Example using the Console class: const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err
@seesource
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to `printf(3)` (the arguments are all passed to util.format()). const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout See util.format() for more information.
@sincev0.1.100
log
("Labeler server running on port 14831");
} });

The LabelerServer class takes an object argument with the following properties:

  • did: The DID of the labeler account.
  • signingKey: The signing key used to sign labels. This should be the same key you received when setting up the labeler, if you used the setup CLI command.
  • auth (optional): A function that takes a DID and returns a boolean indicating whether the DID is authorized to emit labels. If not provided, only the labeler account can emit labels.
  • dbPath (optional): The path to the database file. Defaults to ./labels.db.

After initializing the server, call the start method with the port number to listen on and a callback that will be called when the server starts. If an error occurs, the callback will be called with the error.

Once your labeler is running, you will need to point your labeler’s domain to the server and set up SSL. You can use a tool like Caddy to do this.

If all has gone well, you’re ready to start labeling content!

const server: LabelerServerserver.LabelerServer.createLabel(label: CreateLabelData): Promise<SavedLabel>
Create and insert a label into the database, emitting it to subscribers.
@paramlabel The label to create.@returnsThe created label.
createLabel
({
// If you're labeling a record, this should be an at:// URI pointing to the record. // If you're labeling an account, this should be the account's DID. CreateLabelData.uri: string
The subject of the label. If labeling an account, this should be a string beginning with did:.
uri
: "did:plc:427uaandx74txvzakxthrjwu",
// Assuming you've previously defined a label with the identifier "cool-cat". CreateLabelData.val: string
The label value.
val
: "cool-cat",
});

To undo a label, emit the same label with the neg property set to true to negate the label.

const server: LabelerServerserver.LabelerServer.createLabel(label: CreateLabelData): Promise<SavedLabel>
Create and insert a label into the database, emitting it to subscribers.
@paramlabel The label to create.@returnsThe created label.
createLabel
({
CreateLabelData.uri: string
The subject of the label. If labeling an account, this should be a string beginning with did:.
uri
: "did:plc:427uaandx74txvzakxthrjwu",
CreateLabelData.val: string
The label value.
val
: "cool-cat",
CreateLabelData.neg?: boolean | undefined
Whether this label is negating a previous instance of this label applied to the same subject.
neg
: true
});

Next, read on to learn about Automated Labeling.