Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions plugins/bytedance/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Hermes ByteDance / TikTok Integration

Federated platform plugins for [Hermes Agent](https://hermes-agent.nousresearch.com)
connecting to **TikTok Business Messaging**, **TikTok Organic / Business operations**,
and **Douyin Open Platform** — without erasing their distinct regional, identity,
scope, and policy boundaries.

## What this is

A standalone Python distribution (`hermes-bytedance`) exposing three independently
discoverable Hermes plugin entry points:

| Plugin entry point | Surface | Hermes form |
|---|---|---|
| `tiktok-business` | TikTok Business Messaging API | Gateway platform plugin |
| `douyin` | Douyin Open Platform IM + content APIs | Gateway platform plugin |
| `bytedance-ops` | TikTok Organic / creator publishing / Douyin content ops | Operations tool plugin |

**Shared runtime** (mechanics only — never provider truth):

- Bounded async HTTP client with timeout/retry framework
- Profile/account token broker (no cross-profile fallback)
- Webhook intake with durable composite-key deduplication
- Bounded media broker with SSRF boundary
- Profile-scoped SQLite state store with migrations
- Observability (redacted metrics + logs)
19 changes: 19 additions & 0 deletions plugins/bytedance/SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Security Policy

## Reporting a vulnerability

Security issues in `hermes-bytedance` should be reported following the
Hermes Agent security process. Do **not** open public issues for
vulnerabilities in provider credential handling, webhook verification,
token caching, or media retrieval.

## Design invariants

- No cross-profile secret fallback: each named profile reads only its own config.
- Composite idempotency key `(profile, route, provider, account_alias, event_id)`.
- Provider webhook signature/challenge is verified on raw bytes before any JSON mutation.
- No outbound DM bypasses the provider-specific capability/window policy engine.
- No public content publish occurs without a durable, exact-payload approval record.
- Media retrieval blocks private, loopback, link-local, metadata-service, and
disallowed IP ranges unless the provider CDN origin is allowlisted.
- Tokens, signatures, raw headers, and message bodies are excluded from logs by default.
1 change: 1 addition & 0 deletions plugins/bytedance/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""ByteDance shared plugin package for Hermes Agent."""
49 changes: 49 additions & 0 deletions plugins/bytedance/contracts/normalized-event.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Normalized Event",
"description": "Canonical event shape emitted by all bytedance webhook ingress shells",
"type": "object",
"required": ["provider", "event_id", "event_type", "conversation_id", "sender_id", "message_type", "payload", "received_at", "raw_sha256"],
"properties": {
"provider": {
"type": "string",
"enum": ["tiktok_business", "douyin"],
"description": "The provider that emitted this event"
},
"event_id": {
"type": "string",
"description": "Globally unique event identifier (composite idempotency key)"
},
"event_type": {
"type": "string",
"description": "Normalized event type (message, im_receive_msg, im_enter_direct_msg, etc.)"
},
"conversation_id": {
"type": "string",
"description": "Provider conversation identifier"
},
"sender_id": {
"type": "string",
"description": "Provider sender identifier"
},
"message_type": {
"type": "string",
"description": "Normalized message type (text, image, video, etc.)"
},
"payload": {
"type": "object",
"description": "Original provider payload (redacted)",
"additionalProperties": true
},
"received_at": {
"type": "string",
"format": "date-time",
"description": "When the event was received by the ingress"
},
"raw_sha256": {
"type": "string",
"description": "SHA-256 of the original raw webhook body"
}
},
"additionalProperties": false
}
80 changes: 80 additions & 0 deletions plugins/bytedance/contracts/publish-intent.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Publish Intent",
"description": "Immutable publish intent record stored in the approval ledger",
"type": "object",
"required": [
"intent_id", "profile", "provider", "account_alias",
"actor_id", "payload_json", "payload_sha256",
"state", "created_at", "expires_at"
],
"properties": {
"intent_id": {
"type": "string",
"description": "Deterministic identifier for this intent"
},
"profile": {
"type": "string",
"description": "Hermes profile under which the intent was created"
},
"provider": {
"type": "string",
"enum": ["tiktok_business", "tiktok_creator", "douyin"],
"description": "Provider namespace"
},
"account_alias": {
"type": "string",
"description": "Local account alias"
},
"actor_id": {
"type": "string",
"description": "The user who prepared the intent"
},
"payload_json": {
"type": "string",
"description": "Canonical JSON serialization of the original payload (all keys, null if empty)"
},
"payload_sha256": {
"type": "string",
"description": "SHA-256 of the canonical payload — tampering invalidates the intent"
},
"preview_json": {
"type": "string",
"description": "Human-readable preview (may include redacted content)"
},
"state": {
"type": "string",
"enum": ["DRAFT", "VALIDATED", "AWAITING_APPROVAL", "APPROVED", "COMMITTING", "SUBMITTED", "PUBLISHED", "FAILED", "REJECTED", "EXPIRED"],
"description": "State machine state"
},
"created_at": {
"type": "number",
"description": "Unix timestamp of creation"
},
"expires_at": {
"type": "number",
"description": "Unix timestamp of expiry"
},
"approved_at": {
"type": ["number", "null"],
"description": "When approved (null if not approved)"
},
"committed_at": {
"type": ["number", "null"],
"description": "When committed (null if not committed)"
},
"provider_job_id": {
"type": ["string", "null"],
"description": "Provider-side job/publish ID after submission"
},
"provider_status": {
"type": ["string", "null"],
"description": "Provider-side status after submission"
},
"last_error": {
"type": ["string", "null"],
"description": "Error message if the intent failed"
}
},
"additionalProperties": false
}
9 changes: 9 additions & 0 deletions plugins/bytedance/plugin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: bytedance-shared
label: ByteDance Shared
kind: standalone
version: 1.0.0
description: >
Shared infrastructure for ByteDance (TikTok/Douyin) platform plugins:
bounded HTTP client, SQLite state store, webhook ingress, token broker,
rate limiting, observability, media broker, and immutable approval ledger.
author: Hermes Agent contributors
1 change: 1 addition & 0 deletions plugins/bytedance/shared/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Shared runtime package for ByteDance (TikTok/Douyin) plugins."""
Loading
Loading