TypeScript SDK for creating and publishing ATTN Protocol events on Nostr.
The ATTN SDK provides type-safe event creation and publishing for the ATTN Protocol. It complements the attn framework (which receives/processes events) by providing event creation and publishing capabilities.
The SDK depends on @attn/ts-core for shared constants and type definitions. Event kind constants are available from the core package and are used internally by all event builders to ensure consistency across the ATTN Protocol ecosystem.
npm install @attn/ts-sdk
import { AttnSdk } from "@attn/ts-sdk";
import { ATTN_EVENT_KINDS } from "@attn/ts-core";
// Initialize SDK with private key (hex or nsec format)
const sdk = new AttnSdk({
private_key: "your_private_key_here", // hex or nsec
});
// Note: Block events (kind 38808) are now published by City Protocol (@city/clock)
// ATTN SDK provides a deprecated create_block method for backwards compatibility
// For new implementations, use @city/clock for block event publishing
// Create and publish a PROMOTION event that matches ATTN-01
const promotion_event = sdk.create_promotion({
promotion_id: "promotion-001", // d tag + content identifier
duration: 30_000, // milliseconds
bid: 5_000, // sats for the full duration
event_id: "video_event_id_here",
description: "Watch my amazing content",
call_to_action: "Watch Now",
call_to_action_url: "https://example.com/watch",
marketplace_coordinate: "38188:marketplace_pubkey:marketplace_001",
video_coordinate: "34236:video_author_pubkey:video_d_tag",
billboard_coordinate: "38288:billboard_pubkey:billboard_001",
marketplace_pubkey: "marketplace_pubkey_hex",
promotion_pubkey: sdk.get_public_key(),
relays: ["wss://relay.attnprotocol.org"],
kind: 34236, // promoted kind (k tag)
url: "https://example.com/promotion",
marketplace_id: "marketplace_001",
block_height: 862626, // sets the t tag per ATTN-01
});
// Publish to a single relay
await sdk.publish(promotion_event, "wss://relay.attnprotocol.org");
// Or publish to multiple relays
await sdk.publish_to_multiple(promotion_event, [
"wss://relay.attnprotocol.org",
"wss://relay.attnprotocol.org",
]);
Every builder implements the schemas and tag layout defined in @attn-protocol/packages/protocol/docs/ATTN-01.md. Content fields live in the JSON payload, while routing/indexing values (identifiers, block height, coordinates) ride inside Nostr tags. Always pass the current Bitcoin block_height so the SDK can emit the t tag that powers block-synchronized filtering.
Note: All event builders use constants from @attn/ts-core internally. You can import ATTN_EVENT_KINDS to reference event kinds in your code:
import { ATTN_EVENT_KINDS } from "@attn/ts-core";
// Check event kind
if (event.kind === ATTN_EVENT_KINDS.PROMOTION) {
// Handle promotion event
}
| Builder Method | Parameter Type | Return Type |
|---|---|---|
create_block() |
BlockEventParams |
Event |
create_marketplace() |
MarketplaceEventParams |
Event |
create_billboard() |
BillboardEventParams |
Event |
create_promotion() |
PromotionEventParams |
Event |
create_attention() |
AttentionEventParams |
Event |
create_match() |
MatchEventParams |
Event |
create_billboard_confirmation_event() |
BillboardConfirmationEventParams |
Event |
create_attention_confirmation_event() |
AttentionConfirmationEventParams |
Event |
create_marketplace_confirmation_event() |
MarketplaceConfirmationEventParams |
Event |
| Field | Type | Example | Notes |
|---|---|---|---|
block_height |
number |
862626 |
Bitcoin block height |
duration |
number |
30000 |
Milliseconds |
bid / ask |
number |
5000 |
Total satoshis for duration |
coordinate |
string |
"38188:pubkey:id" |
Format: <kind>:<pubkey>:<identifier> |
promotion_id |
string |
"promotion-001" |
Unique identifier |
marketplace_id |
string |
"marketplace_001" |
Unique identifier |
billboard_id |
string |
"billboard_001" |
Unique identifier |
attention_id |
string |
"attention_001" |
Unique identifier |
match_id |
string |
"match_001" |
Unique identifier |
kind_list |
number[] |
[34236, 1, 30023] |
Array of event kind numbers |
relay_list |
string[] |
["wss://relay.example.com"] |
Array of relay URLs |
Each subsection restates the ATTN-01 content + tag requirements so builders stay in lockstep with the block-synchronized snapshot model. When the SDK does not yet expose a specific field/tag (for example, relay_list inside the PROMOTION content), treat that as a TODO before publishing—extend the helper or compose the JSON manually so every required field lands on-chain of record.
Note: Block events are now published by City Protocol (kind 38808). The ATTN SDK’s
create_blockmethod is deprecated and kept only for backwards compatibility. For new implementations, use@city/clockfrom the City Protocol.
City Protocol block events (CITY-01) include:
block_height, block_hash, block_time, previous_hashdifficulty, tx_count, size, weight, version, merkle_root, nonceref_clock_pubkey - The City clock pubkey that published the eventref_block_id - The block identifier (format: org.cityprotocol:block:<height>:<hash>)For creating block events, see the City Protocol documentation.
ATTN_EVENT_KINDS.PROMOTION / 38388)ATTN-01 content requirements:
duration, bid, event_id, description?, call_to_action, call_to_action_urlmarketplace_pubkey, promotion_pubkey, marketplace_id, promotion_idATTN-01 tag requirements:
["d", promotion_id]["t", block_height]["a", marketplace_coordinate], ["a", video_coordinate], ["a", billboard_coordinate]["p", marketplace_pubkey], ["p", promotion_pubkey]["r", relay_url]["k", kind]["u", url]const promotion_event = sdk.create_promotion({
promotion_id: "promotion-001",
duration: 30_000,
bid: 5_000,
event_id: "video_event_id_here",
description: "Watch my amazing content",
call_to_action: "Watch Now",
call_to_action_url: "https://example.com/watch",
marketplace_coordinate: "38188:marketplace_pubkey:marketplace_001",
video_coordinate: "34236:video_author_pubkey:video_d_tag",
billboard_coordinate: "38288:billboard_pubkey:billboard_001",
marketplace_pubkey: "marketplace_pubkey_hex",
promotion_pubkey: sdk.get_public_key(),
relays: ["wss://relay.attnprotocol.org"],
kind: 34236,
url: "https://example.com/promotion",
marketplace_id: "marketplace_001",
block_height: 862626,
});
ATTN_EVENT_KINDS.ATTENTION / 38488)ATTN-01 content requirements:
ask, min_duration, max_duration, kind_list, relay_listattention_pubkey, marketplace_pubkey, attention_id, marketplace_idblocked_promotions_id, blocked_promoters_id (NIP-51 list identifiers)ATTN-01 tag requirements:
["d", attention_id]["t", block_height]["a", marketplace_coordinate]["a", blocked_promotions_coordinate] (30000:<attention_pubkey>:org.attnprotocol:promotion:blocked)["a", blocked_promoters_coordinate] (30000:<attention_pubkey>:org.attnprotocol:promoter:blocked)["p", attention_pubkey], ["p", marketplace_pubkey]["r", relay_url]["k", kind]const attention_event = sdk.create_attention({
attention_id: "attention-001",
ask: 3_000,
min_duration: 15_000,
max_duration: 60_000,
kind_list: [34236, 1, 30023],
relay_list: ["wss://relay.attnprotocol.org"],
marketplace_coordinate: "38188:marketplace_pubkey:marketplace_001",
blocked_promotions_coordinate: "30000:attention_pubkey:org.attnprotocol:promotion:blocked",
blocked_promoters_coordinate: "30000:attention_pubkey:org.attnprotocol:promoter:blocked",
blocked_promotions_id: "org.attnprotocol:promotion:blocked",
blocked_promoters_id: "org.attnprotocol:promoter:blocked",
attention_pubkey: sdk.get_public_key(),
marketplace_pubkey: "marketplace_pubkey_hex",
relays: ["wss://relay.attnprotocol.org"],
kinds: [34236, 1, 30023],
marketplace_id: "marketplace_001",
block_height: 862626,
});
ATTN_EVENT_KINDS.MATCH / 38888)ATTN-01 content requirements:
ask, bid, duration, kind_list, relay_listmarketplace_pubkey, promotion_pubkey, attention_pubkey, billboard_pubkeymarketplace_id, billboard_id, promotion_id, attention_idATTN-01 tag requirements:
["d", match_id]["t", block_height]["a", marketplace_coordinate], ["a", billboard_coordinate], ["a", promotion_coordinate], ["a", attention_coordinate]["p", marketplace_pubkey], ["p", promotion_pubkey], ["p", attention_pubkey], ["p", billboard_pubkey]["r", relay_url]["k", kind]block_height is missing.const match_event = sdk.create_match({
match_id: "match-001",
marketplace_coordinate: "38188:marketplace_pubkey:marketplace_001",
billboard_coordinate: "38288:billboard_pubkey:billboard_001",
promotion_coordinate: "38388:promotion_pubkey:promotion_001",
attention_coordinate: "38488:attention_pubkey:attention_001",
marketplace_pubkey: "marketplace_pubkey_hex",
promotion_pubkey: "promotion_pubkey_hex",
attention_pubkey: "attention_pubkey_hex",
billboard_pubkey: "billboard_pubkey_hex",
marketplace_id: "marketplace_001",
billboard_id: "billboard_001",
promotion_id: "promotion_001",
attention_id: "attention_001",
ask: 3_000,
bid: 5_000,
duration: 30_000,
kind_list: [34236],
relay_list: ["wss://relay.attnprotocol.org"],
relays: ["wss://relay.attnprotocol.org"],
block_height: 862626,
});
ATTN_EVENT_KINDS.MARKETPLACE / 38188)ATTN-01 content requirements:
name, description, image?, kind_list, relay_list, url?admin_pubkey, admin_email?, min_duration?, max_duration?marketplace_pubkey, marketplace_idATTN-01 tag requirements:
["d", marketplace_id]["t", block_height]["k", kind]["p", marketplace_pubkey]["r", relay_url]["u", website_url]const marketplace_event = sdk.create_marketplace({
marketplace_id: "marketplace-001",
name: "Example Marketplace",
description: "Decentralized attention marketplace",
kind_list: [34236, 1, 30023],
relay_list: ["wss://relay.attnprotocol.org"],
admin_pubkey: sdk.get_public_key(),
marketplace_pubkey: sdk.get_public_key(),
image: "https://example.com/image.png",
url: "https://marketplace.example.com",
website_url: "https://marketplace.example.com",
admin_email: "admin@example.com",
min_duration: 15_000,
max_duration: 60_000,
block_height: 862626,
});
ATTN_EVENT_KINDS.BILLBOARD / 38288)ATTN-01 content requirements:
name, description?billboard_pubkey, marketplace_pubkeybillboard_id, marketplace_idATTN-01 tag requirements:
["d", billboard_id]["t", block_height]["a", marketplace_coordinate]["p", billboard_pubkey], ["p", marketplace_pubkey]["r", relay_url]["k", kind]["u", url]const billboard_event = sdk.create_billboard({
billboard_id: "billboard-001",
name: "My Billboard",
description: "A great billboard for promotions",
marketplace_coordinate: "38188:marketplace_pubkey:marketplace_001",
billboard_pubkey: sdk.get_public_key(),
marketplace_pubkey: "marketplace_pubkey_hex",
relays: ["wss://relay.attnprotocol.org"],
kind: 34236,
url: "https://billboard.example.com",
marketplace_id: "marketplace_001",
block_height: 862626,
});
ATTN_EVENT_KINDS.BILLBOARD_CONFIRMATION / 38588)ATTN-01 content requirements:
block, pricemarketplace_event_id, promotion_event_id, attention_event_id, match_event_idmarketplace_pubkey, promotion_pubkey, attention_pubkey, billboard_pubkeymarketplace_id, promotion_id, attention_id, match_idATTN-01 tag requirements:
["a", marketplace_coordinate], ["a", promotion_coordinate], ["a", attention_coordinate], ["a", match_coordinate]["e", marketplace_event_id], ["e", promotion_event_id], ["e", attention_event_id], ["e", match_event_id]["p", marketplace_pubkey], ["p", promotion_pubkey], ["p", attention_pubkey], ["p", billboard_pubkey]["r", relay_url]["t", block_height]["u", url]import { create_billboard_confirmation_event } from "@attn/ts-sdk";
import { nip19 } from "nostr-tools";
const decoded = nip19.decode("nsec1...");
const private_key = decoded.data as Uint8Array;
const billboard_confirmation = create_billboard_confirmation_event(
private_key,
{
block: 862626,
price: 2_000,
marketplace_ref: "marketplace_event_id",
promotion_ref: "promotion_event_id",
attention_ref: "attention_event_id",
match_ref: "match_event_id",
marketplace_coordinate: "38188:marketplace_pubkey:marketplace_001",
promotion_coordinate: "38388:promotion_pubkey:promotion_001",
attention_coordinate: "38488:attention_pubkey:attention_001",
match_coordinate: "38888:marketplace_pubkey:match_001",
marketplace_pubkey: "marketplace_pubkey_hex",
promotion_pubkey: "promotion_pubkey_hex",
attention_pubkey: "attention_pubkey_hex",
billboard_pubkey: "billboard_pubkey_hex",
marketplace_id: "marketplace_001",
promotion_id: "promotion_001",
attention_id: "attention_001",
match_id: "match_001",
relays: ["wss://relay.attnprotocol.org"],
url: "https://billboard.example.com/confirmation",
}
);
ATTN_EVENT_KINDS.ATTENTION_CONFIRMATION / 38688)ATTN-01 content requirements:
block, price, sats_delivered, optional proof_payloadmarketplace_event_id, promotion_event_id, attention_event_id, match_event_idmarketplace_pubkey, promotion_pubkey, attention_pubkey, billboard_pubkeymarketplace_id, promotion_id, attention_id, match_idATTN-01 tag requirements:
["a", marketplace_coordinate], ["a", promotion_coordinate], ["a", attention_coordinate], ["a", match_coordinate]["e", marketplace_event_id], ["e", promotion_event_id], ["e", attention_event_id], ["e", match_event_id]["p", marketplace_pubkey], ["p", promotion_pubkey], ["p", attention_pubkey], ["p", billboard_pubkey]["r", relay_url]["t", block_height]["u", url]import { create_attention_confirmation_event } from "@attn/ts-sdk";
import { nip19 } from "nostr-tools";
const decoded = nip19.decode("nsec1...");
const private_key = decoded.data as Uint8Array;
const attention_confirmation = create_attention_confirmation_event(
private_key,
{
block: 862626,
price: 3_000,
sats_delivered: 3_000,
proof_payload: "viewer_proof_data",
marketplace_ref: "marketplace_event_id",
promotion_ref: "promotion_event_id",
attention_ref: "attention_event_id",
match_ref: "match_event_id",
marketplace_coordinate: "38188:marketplace_pubkey:marketplace_001",
promotion_coordinate: "38388:promotion_pubkey:promotion_001",
attention_coordinate: "38488:attention_pubkey:attention_001",
match_coordinate: "38888:marketplace_pubkey:match_001",
marketplace_pubkey: "marketplace_pubkey_hex",
promotion_pubkey: "promotion_pubkey_hex",
attention_pubkey: "attention_pubkey_hex",
billboard_pubkey: "billboard_pubkey_hex",
marketplace_id: "marketplace_001",
promotion_id: "promotion_001",
attention_id: "attention_001",
match_id: "match_001",
relays: ["wss://relay.attnprotocol.org"],
url: "https://viewer.example.com/confirmation",
}
);
ATTN_EVENT_KINDS.MARKETPLACE_CONFIRMATION / 38788)ATTN-01 content requirements:
block, duration, ask, bid, price, sats_settledpayout_breakdown.viewer?, payout_breakdown.billboard?marketplace_event_id, promotion_event_id, attention_event_id, match_event_idbillboard_confirmation_event_id, attention_confirmation_event_idmarketplace_pubkey, promotion_pubkey, attention_pubkey, billboard_pubkeymarketplace_id, promotion_id, attention_id, match_idATTN-01 tag requirements:
["a", marketplace_coordinate], ["a", promotion_coordinate], ["a", attention_coordinate], ["a", match_coordinate]["e", marketplace_event_id], ["e", promotion_event_id], ["e", attention_event_id], ["e", match_event_id]["e", billboard_confirmation_event_id], ["e", attention_confirmation_event_id]["p", marketplace_pubkey], ["p", promotion_pubkey], ["p", attention_pubkey], ["p", billboard_pubkey]["r", relay_url]["t", block_height]["u", url]import { create_marketplace_confirmation_event } from "@attn/ts-sdk";
import { nip19 } from "nostr-tools";
const decoded = nip19.decode("nsec1...");
const private_key = decoded.data as Uint8Array;
const marketplace_confirmation = create_marketplace_confirmation_event(
private_key,
{
block: 862626,
duration: 30_000,
ask: 3_000,
bid: 5_000,
price: 5_000,
sats_settled: 5_000,
payout_breakdown: {
viewer: 3_000,
billboard: 2_000,
},
marketplace_ref: "marketplace_event_id",
promotion_ref: "promotion_event_id",
attention_ref: "attention_event_id",
match_ref: "match_event_id",
billboard_confirmation_ref: "billboard_confirmation_event_id",
attention_confirmation_ref: "attention_confirmation_event_id",
marketplace_coordinate: "38188:marketplace_pubkey:marketplace_001",
promotion_coordinate: "38388:promotion_pubkey:promotion_001",
attention_coordinate: "38488:attention_pubkey:attention_001",
match_coordinate: "38888:marketplace_pubkey:match_001",
marketplace_pubkey: "marketplace_pubkey_hex",
promotion_pubkey: "promotion_pubkey_hex",
attention_pubkey: "attention_pubkey_hex",
billboard_pubkey: "billboard_pubkey_hex",
marketplace_id: "marketplace_001",
promotion_id: "promotion_001",
attention_id: "attention_001",
match_id: "match_001",
relays: ["wss://relay.attnprotocol.org"],
url: "https://marketplace.example.com/confirmation",
}
);
Note: Confirmation event builders require a Uint8Array private key. You can decode from hex or nsec format as shown above.
const result = await sdk.publish(event, "wss://relay.attnprotocol.org");
console.log(result.success); // true or false
console.log(result.event_id); // Event ID
console.log(result.error); // Error message if failed
const results = await sdk.publish_to_multiple(event, [
"wss://relay.attnprotocol.org",
"wss://relay.attnprotocol.org",
]);
console.log(results.success_count); // Number of successful publishes
console.log(results.failure_count); // Number of failed publishes
console.log(results.results); // Array of individual results
The SDK supports both hex and nsec (nip19) private key formats:
// Hex format (64 hex characters)
const sdk1 = new AttnSdk({
private_key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
});
// Nsec format (nip19 encoded)
const sdk2 = new AttnSdk({
private_key: "nsec1...",
});
The SDK validates configuration in the constructor:
"Invalid nsec format" - Nsec decoding failed or wrong type"Invalid hex private key: must be 64 hex characters" - Hex string wrong length"Invalid hex private key format" - Hex string invalid formatAll validation happens at construction time, ensuring the SDK instance is always in a valid state.
// Import SDK for creating events
import { AttnSdk } from "@attn/ts-sdk";
// Import framework for receiving/processing events
import { Attn } from "@attn/ts-framework";
// Initialize SDK with private key (hex or nsec format)
const sdk = new AttnSdk({
private_key: "your_private_key_here", // hex or nsec
});
// Note: Block events (kind 38808) are now published by City Protocol (@city/clock)
// ATTN Protocol services subscribe to City Protocol block events for timing
// Initialize framework for receiving events
const attn = new Attn({
relays: ["wss://relay.attnprotocol.org"],
private_key: private_key_uint8array, // Uint8Array for NIP-42 auth
clock_pubkeys: [clock_pubkey_hex], // Trusted City Protocol clock services
});
// Register hook handlers
attn.on_new_block(async (context) => {
console.log(`New block: ${context.block_height} (hash: ${context.block_hash})`);
// Process block-synchronized tasks here
});
attn.on_new_promotion(async (context) => {
console.log("New promotion received:", context.event);
// Handle promotion event
});
// Connect to relays
try {
await attn.connect();
console.log("Connected to relays");
} catch (error) {
console.error("Failed to connect:", error);
}
import { AttnSdk } from "@attn/ts-sdk";
const sdk = new AttnSdk({ private_key: "your_private_key" });
// 1. Create marketplace
const marketplace = sdk.create_marketplace({
marketplace_id: "marketplace-001",
name: "Example Marketplace",
description: "Decentralized attention marketplace",
kind_list: [34236],
relay_list: ["wss://relay.attnprotocol.org"],
admin_pubkey: sdk.get_public_key(),
marketplace_pubkey: sdk.get_public_key(),
block_height: 862626,
});
// 2. Create billboard
const billboard = sdk.create_billboard({
billboard_id: "billboard-001",
name: "My Billboard",
marketplace_coordinate: `38188:${sdk.get_public_key()}:marketplace-001`,
billboard_pubkey: sdk.get_public_key(),
marketplace_pubkey: sdk.get_public_key(),
relays: ["wss://relay.attnprotocol.org"],
kind: 34236,
url: "https://billboard.example.com",
marketplace_id: "marketplace-001",
block_height: 862626,
});
// 3. Create promotion
const promotion = sdk.create_promotion({
promotion_id: "promotion-001",
duration: 30_000,
bid: 5_000,
event_id: "video_event_id_here",
description: "Watch my amazing content",
call_to_action: "Watch Now",
call_to_action_url: "https://example.com/watch",
marketplace_coordinate: `38188:${sdk.get_public_key()}:marketplace-001`,
billboard_coordinate: `38288:${sdk.get_public_key()}:billboard-001`,
video_coordinate: "34236:video_author_pubkey:video_d_tag",
marketplace_pubkey: sdk.get_public_key(),
promotion_pubkey: sdk.get_public_key(),
relays: ["wss://relay.attnprotocol.org"],
kind: 34236,
url: "https://example.com/promotion",
marketplace_id: "marketplace-001",
block_height: 862626,
});
// 4. Publish to relays
await sdk.publish_to_multiple(promotion, ["wss://relay.attnprotocol.org"]);
// Matching criteria (all must be true):
// 1. Economic: promotion.bid >= attention.ask
// 2. Duration: attention.min_duration <= promotion.duration <= attention.max_duration
// 3. Kind: promotion.kind in attention.kind_list
// 4. Block list: promotion not in attention.blocked_promotions AND promoter not in attention.blocked_promoters
function is_match_valid(
promotion: PromotionEvent,
attention: AttentionEvent,
blocked_promotions: string[],
blocked_promoters: string[]
): boolean {
// Economic compatibility
if (promotion.bid < attention.ask) {
return false;
}
// Duration compatibility
if (
promotion.duration < attention.min_duration ||
promotion.duration > attention.max_duration
) {
return false;
}
// Kind compatibility
if (!attention.kind_list.includes(promotion.kind)) {
return false;
}
// Block list checks
const promotion_coordinate = `38388:${promotion.promotion_pubkey}:${promotion.promotion_id}`;
if (blocked_promotions.includes(promotion_coordinate)) {
return false;
}
if (blocked_promoters.includes(promotion.promotion_pubkey)) {
return false;
}
return true;
}
// Create MATCH event when valid
if (is_match_valid(promotion, attention, blocked_promotions, blocked_promoters)) {
const match = sdk.create_match({
match_id: "match-001",
marketplace_coordinate: `38188:${marketplace_pubkey}:${marketplace_id}`,
billboard_coordinate: `38288:${billboard_pubkey}:${billboard_id}`,
promotion_coordinate: `38388:${promotion_pubkey}:${promotion_id}`,
attention_coordinate: `38488:${attention_pubkey}:${attention_id}`,
marketplace_pubkey,
promotion_pubkey,
attention_pubkey,
billboard_pubkey,
marketplace_id,
billboard_id,
promotion_id,
attention_id,
ask: attention.ask,
bid: promotion.bid,
duration: promotion.duration,
kind_list: attention.kind_list,
relay_list: attention.relay_list,
relays: attention.relay_list,
block_height: current_block_height,
});
}
The SDK includes validation utilities for event validation:
import {
validate_block_height,
validate_json_content,
validate_pubkey,
} from "@attn/ts-sdk";
const result = validate_block_height(event);
if (!result.valid) {
console.error(result.message);
}
All event parameters are fully typed with TypeScript interfaces:
import type {
PromotionEventParams,
AttentionEventParams,
MatchEventParams,
} from "@attn/ts-sdk";
All events support Bitcoin block height for block-synchronized operations:
const event = sdk.create_promotion({
// ... other params
block_height: 862626, // Current Bitcoin block height
});
All publishing methods return detailed error information:
const result = await sdk.publish(event, relay_url);
if (!result.success) {
console.error(`Failed to publish: ${result.error}`);
}
MIT