Skip to content

feat(api): add ActivityPub schema parity proof#335

Merged
skulidropek merged 4 commits into
mainfrom
issue-334
May 21, 2026
Merged

feat(api): add ActivityPub schema parity proof#335
skulidropek merged 4 commits into
mainfrom
issue-334

Conversation

@skulidropek
Copy link
Copy Markdown
Member

@skulidropek skulidropek commented May 20, 2026

Summary

  • Adds runtime Effect Schema contracts for ActivityPub/ForgeFed documents used by federation.
  • Derives federation TypeScript contract types from the schemas instead of unchecked duplicate aliases.
  • Validates local JSON-LD actor/collection responses with strict exact decoding before serialization.
  • Adds Mastodon-compatible /federation/followers?page=1 as an exact OrderedCollectionPage.
  • Tightens CodeRabbit review gaps: actor-specific JSON-LD contexts, JSON-safe boundary values, literal actor extension variants, typed interactionPolicy, exact excess-property rejection, property tests, and unsupported followers page rejection.

Closes #334.

Proof: Exact 1:1 Type Correspondence

Sources

Invariants

  • Schema-as-source-of-truth: ActivityPubPerson, ActivityPubOrderedCollection, ActivityPubOrderedCollectionPage, ActivityPubFollowActivity, ForgeFedTicket, and ActivityPubPublicKey are Schema.Schema.Type<typeof ...Schema>. Therefore the public TypeScript type and runtime decoder are derived from the same schema contract.
  • Exact decode mode: federation proof paths use exactActivityPubParseOptions = { onExcessProperty: "error" }. The HTTP response boundary and parity tests call Schema.decodeUnknown(..., exactActivityPubParseOptions), so extra fields fail instead of being silently dropped.
  • No unchecked public schema values: public ActivityPub/ForgeFed schema fields do not use Schema.Unknown; collection orderedItems, ticket raw, and ticket attachment are bounded by JsonValue.
  • Exact actor variants: ActivityPubPersonSchema is a union of exact local actor shape and exact Mastodon issue actor shape. The local actor requires exactly the local protocol fields emitted by docker-git. The Mastodon actor requires exactly the issue fixture fields and rejects top-level extras such as icon, image, devices, alsoKnownAs, or any other field not present in the issue JSON.
  • Actor context exactness: local actors decode only with [ActivityStreams, Security, ForgeFed]. Mastodon actors decode only with [ActivityStreams, Security, SocialWeb WebFinger, extension-object]; the extension object requires all 22 issue keys, requires mapping objects to contain @id and literal @type: "@id", and rejects missing or extra context terms.
  • Actor extension shape proof: attachment requires { type: "PropertyValue", name, value }; tag is a discriminated union of { type: "Hashtag", name, href } and { type: "Emoji", id, name, icon }; nested image objects require { type: "Image", url }. Empty actor extension objects and wrong literal variants decode to Left.
  • Interaction policy shape proof: interactionPolicy accepts only Mastodon canFeature/canQuote blocks, and each block must contain automaticApproval or manualApproval. Empty policy objects, arbitrary policy keys, and arbitrary nested approval objects decode to Left.
  • Exact collection/page variants: ActivityPubOrderedCollectionSchema is a union of exact local collection, exact local followers collection, and exact Mastodon followers collection. ActivityPubOrderedCollectionPageSchema is a union of exact local followers page and exact Mastodon followers page.
  • Mastodon followers page parity: Mastodon followers?page=1 decodes only as @context: ActivityStreams, type: "OrderedCollectionPage", id, totalItems, partOf, next, and orderedItems: string[]. Embedded objects, prev, and other unlisted keys decode to Left.
  • Outbound validation: for every local JSON-LD federation response d in { actor, outbox, followers, followers?page=1, following, liked }, the HTTP handler returns 200 only after strict Schema.decodeUnknown(S_d, exactActivityPubParseOptions)(d) succeeds.
  • Literal and required-field proof: generated property tests prove that correct literals decode and wrong literals or missing required fields are rejected for Person, OrderedCollection, and OrderedCollectionPage.
  • Exact fixture key proof: Mastodon fixtures are decoded and tests assert keys(decoded) = keys(fixture), so the schema is not proving success by silently dropping fields.
  • Negative exactness proof: regression tests reject extra actor fields, missing required context extension keys, missing @type mappings, extra context terms, extra collection/page fields, and non-string Mastodon followers page items.
  • Followers pagination: /federation/followers returns OrderedCollection; /federation/followers?page=1 returns OrderedCollectionPage with partOf = /federation/followers; /federation/followers?page=2 fails with typed ApiBadRequestError and HTTP 400.

Theorem

Let DecodeExact(S, x) = Schema.decodeUnknown(S, exactActivityPubParseOptions)(x).

For each supported federation document d, if the API returns d successfully, then there exists a schema S_d such that:

HTTP_200(d) -> DecodeExact(S_d, d) = Right(decoded)
             and keys(decoded) = keys(d)
             and required_fields(S_d) = required_fields(d)
             and literal_type(d) = literal_type(S_d)
             and json_boundary_fields(d) subset JsonValue

For the issue/Mastodon actor fixture f_actor:

DecodeExact(ActivityPubPersonSchema, f_actor) = Right(decoded)
and keys(decoded) = keys(f_actor)
and actor_context(f_actor) = [ActivityStreams, Security, SocialWebWebFinger, Exact22TermContext]

For the issue/Mastodon followers page fixture f_page:

DecodeExact(ActivityPubOrderedCollectionPageSchema, f_page) = Right(decoded)
and keys(decoded) = keys(f_page)
and forall item in f_page.orderedItems: typeof item = string

For invalid payloads x:

extra_field(x)
or missing_required_field(x)
or wrong_literal(x)
or missing_context_term(x)
or extra_context_term(x)
or arbitrary_interaction_policy(x)
or non_string_mastodon_followers_item(x)
  -> DecodeExact(S, x) = Left(error)

So the proof obligation is executable: it is enforced by the HTTP response path, schema-derived TypeScript contracts, exact fixture parity tests, property tests, actor-extension counterexample tests, collection/page exactness tests, and negative HTTP tests.

Verification

Latest local verification on commit a5bcc9b:

bun run --cwd packages/api typecheck
bun run --cwd packages/api lint
bun run --cwd packages/api test

Full API test result: 24 test files, 177 tests passed.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Добавлены схемы валидации для JSON/JSON‑LD, ActivityPub/ForgeFed (включая расширения Mastodon) и экспортированы в публичный API.
    • Поддержка пагинации подписчиков: явная первая страница (?page=1) и отдельная страница-коллекция.
    • Валидированная генерация JSON‑LD ответов и сохранение "raw" данных тикетов при наличии.
  • Bug Fixes

    • Федеративные эндпоинты возвращают типизированные ответы и дают детальные ошибки при несоответствии схемам.
    • Ошибка при некорректном значении режима страницы для followers.
  • Tests

    • Добавлены паритетные и property‑based тесты для новых схем и федеративных документов.

Walkthrough

PR добавляет effect/Schema-валидаторы для JSON-LD/ActivityPub/ForgeFed, переводит контрактные типы на schema-derived типы, интегрирует валидацию в federation HTTP endpoints (actor/outbox/followers/following/liked), расширяет сервисы федерации для поддержки followers?page=1 и добавляет паритетные и интеграционные тесты.

Changes

Валидация ActivityPub через Effect Schema

Layer / File(s) Summary
Определения ActivityPub схем
packages/api/src/api/activitypub-schema.ts
Новый модуль с полным набором effect/Schema валидаторов: рекурсивный JsonValueSchema, JSON-LD контексты (tuple/union), ForgeFed ticket схемы, ActivityPubPublicKey, ActivityPubPerson, ActivityPubFollowActivity, ActivityPubOrderedCollection и ActivityPubOrderedCollectionPage, плюс exactActivityPubParseOptions.
Миграция типов контрактов на схемы
packages/api/src/api/contracts.ts
Контрактные типы (ActivityForgeFedJsonLdContext, ActorJsonLdContext, ForgeFedTicket, ActivityPubFollowActivity, ActivityPubPerson, ActivityPubOrderedCollection, новый ActivityPubOrderedCollectionPage и локальные Local* типы) стали Schema.Schema.Type<typeof ...Schema>; добавлена константа socialWebWebfingerJsonLdContext.
Переэкспорт схем в публичный API
packages/api/src/api/schema.ts
Добавлен export { ... } from "./activitypub-schema.js" — публичные переэкспортируемые *Schema валидаторы для использования в других модулях.
Валидация JSON-LD в HTTP endpoints
packages/api/src/http.ts
Добавлены validatedJsonLdResponse для схемной валидации ответа и readFollowersPageMode для выбора режима; federation routes (actor, outbox, followers с режимом page, following, liked) теперь используют схемы для валидации payload.
Поддержка пагинации followers
packages/api/src/services/federation.ts
makeFederationFollowersCollection получает поле first: \${context.followers}?page=1`и пустую витрину; добавлена функцияmakeFederationFollowersPageCollectionдля страницы?page=1; введены isJsonValue/readJsonValue, followActivityJson; изменена валидация attachmentи добавлена фиксацияrawвparseTicket`.
Тестирование паритета схем
packages/api/tests/activitypub-schema-parity.test.ts
Позитивные и негативные тесты + property-based проверки для ActivityPubPersonSchema, ActivityPubOrderedCollectionSchema и ActivityPubOrderedCollectionPageSchema на соответствие фикстурам и обязательным полям, включая проверки расширений actor.
Валидация в HTTP конфиг-тестах
packages/api/tests/http-config.test.ts
Добавлены хелперы decodeOrThrow/decodeFederationDocument и интегрированная валидация federation JSON-LD документов в существующие тесты; отдельная проверка followers page как типизированного ActivityPub JSON-LD.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant HTTPHandler
  participant FederationService
  participant SchemaValidator
  Client->>HTTPHandler: GET /federation/followers?page=1
  HTTPHandler->>FederationService: makeFederationFollowersPageCollection(context)
  FederationService->>SchemaValidator: produce raw page document
  SchemaValidator->>HTTPHandler: validated document (ActivityPubOrderedCollectionPageSchema)
  HTTPHandler->>Client: 200 application/activity+json
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • ProverCoderAI/docker-git#295: Дополняет изменения в федерации и контекстах JSON-LD, затрагивает смежную поверхность контрактов/контекстов и может пересекаться с переопределением @context и контрактной типизацией.
🚥 Pre-merge checks | ✅ 7
✅ Passed checks (7 passed)
Check name Status Explanation
Title check ✅ Passed Заголовок точно описывает основное изменение: добавление схем ActivityPub с доказательством паритета, что является главной целью PR.
Description check ✅ Passed Описание полностью соответствует шаблону и содержит все необходимые разделы: Summary, Requirements Alignment и Verification с подробными деталями реализации.
Linked Issues check ✅ Passed Все требования из issue #334 реализованы: строгая валидация JSON-LD @context, структурированные схемы для actor extensions (tag, attachment), типизированная interactionPolicy, JsonValue-bounded fields, property tests, пагинация followers, отказ для неподдерживаемых страниц.
Out of Scope Changes check ✅ Passed Все изменения в масштабе PR напрямую относятся к целям issue #334: добавление ActivityPub схем, валидация responses, пагинация и точная проверка соответствия Mastodon JSON формам.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Requirements Alignment ✅ Passed All issue #334 requirements satisfied: Mastodon parity, @context validation, no Schema.Unknown, followers pagination, validation before serialization, extensions, typed interactionPolicy, tests.
Security Regression ✅ Passed No security regressions found. Query parameters safely validated with exact string matching, no dangerous APIs or user-input execution.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-334

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/api/src/api/activitypub-schema.ts`:
- Around line 63-64: The ActivityPub contract uses Schema.Unknown for fields
attachment, raw and orderedItems (and other occurrences around the 132-143
block), which permits arbitrary values; replace these Schema.Unknown usages with
concrete, JSON-safe schemas that reflect the expected shape (e.g.,
Schema.Object/Schema.Record with typed properties,
Schema.Array(Schema.String|Schema.Object) or Schema.String/Schema.Number as
appropriate) so validation enforces JSON-compatible structure; update the
schemas for attachment, raw, orderedItems (and the other Schema.Unknown
instances) to specific Schema.* types matching the ActivityPub spec for those
fields and adjust any decoding/consumers to handle the stricter types.
- Line 83: ActivityPubPersonSchema currently uses the overly-broad
JsonLdContextSchema for its "`@context`" field; narrow it to an actor-specific
context by replacing the "`@context`": JsonLdContextSchema entry with a new or
existing ActorContextSchema (e.g., ActorContextSchema or
ActivityPubActorContextSchema) that only allows the expected actor/activitypub
contexts/URLs/objects; add or import the ActorContextSchema, update
ActivityPubPersonSchema's "`@context`" to reference it, and ensure the new schema
validates the actor-specific keys/URLs so invalid actor documents are rejected.

In `@packages/api/tests/activitypub-schema-parity.test.ts`:
- Around line 146-233: Add fast-check property tests that assert the decoding
invariants for ActivityPub schemas: use fc.assert + fc.property to generate
objects and verify that Schema.decodeUnknownEither(ActivityPubPersonSchema),
Schema.decodeUnknownEither(ActivityPubOrderedCollectionSchema), and
Schema.decodeUnknownEither(ActivityPubOrderedCollectionPageSchema) succeed when
the generated object has the correct literal "type" and all required fields
present, and fail when either the "type" is incorrect or required fields are
missing; use Either.isRight to assert success and Either.isLeft to assert
failure (same pattern as existing tests) and reuse the expect/validation helpers
(e.g., expectDecodedCoversFixtureKeys or simple Either checks) to keep
assertions consistent.

In `@packages/api/tests/http-config.test.ts`:
- Around line 268-282: Add a negative test next to the existing followers
happy-path: call readFederationDocumentRoute("/federation/followers?page=2")
(after clearFederationState()), parse the response with
parseJsonObject(document.body) and assert document.status is 400 and the parsed
payload contains the expected unsupported-page error message (e.g.,
readField(payload, "message") or appropriate error field equals/contains
"unsupported page"); keep content-type/assertions consistent with how other
error responses are validated in the suite and don't call decodeOrThrow for the
success schema (ActivityPubOrderedCollectionPageSchema) in this case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b50984ac-fd72-4ea2-a995-3599bc746d60

📥 Commits

Reviewing files that changed from the base of the PR and between 9691b4c and 1700485.

📒 Files selected for processing (7)
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/api/contracts.ts
  • packages/api/src/api/schema.ts
  • packages/api/src/http.ts
  • packages/api/src/services/federation.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/tests/http-config.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: E2E (Runtime volumes + SSH)
  • GitHub Check: E2E (Browser command)
  • GitHub Check: E2E (Clone auto-open SSH)
  • GitHub Check: E2E (Clone cache)
  • GitHub Check: E2E (Login context)
  • GitHub Check: E2E (OpenCode)
  • GitHub Check: Lint
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Implement Functional Core, Imperative Shell (FCIS) pattern: CORE layer contains only pure functions with immutable data and mathematical operations; SHELL layer isolates all effects (IO, network, database). Strict dependency direction: SHELL → CORE (never reverse).
Never use any, unknown, eslint-disable, ts-ignore, or as type assertions (except in rigorously justified cases with documentation). Always use exhaustive union type analysis through .exhaustive() pattern matching.
All external dependencies must be wrapped through typed interfaces and injected via Effect-TS Layer pattern. Never call external services directly from CORE functions.
Use monadic composition with Effect-TS for all effects: Effect<Success, Error, Requirements>. Compose effects through pipe() and Effect.flatMap(). Implement dependency injection via Layer pattern. Handle errors without try/catch blocks.
All functions must be pure in the CORE layer: no side effects (logging, console output, IO operations, mutations). Separate all side effects into the SHELL layer.
Use exhaustive pattern matching with Effect.Match instead of switch statements. Example: Match.value(item).pipe(Match.when(...), Match.exhaustive).
Document all functions with comprehensive TSDoc including: @pure (true/false), @effect (required services), @invariant (mathematical invariants), @precondition, @postcondition, @complexity (time and space), @throws Never (errors must be typed in Effect).
Use functional comment markers for code clarity: CHANGE (brief description), WHY (mathematical/architectural justification), QUOTE(ТЗ) (requirement citation), REF (RTM or message ID), SOURCE (external source with quote), FORMAT THEOREM (∀x ∈ Domain: P(x) → Q(f(x))), PURITY (CORE|SHELL), EFFECT (Effect type signature), INVARIANT (mathematical invariant), COMPLEXITY (time/space).
Define all external service dependencies as Context.Tag classes with fully typed methods returning Effect types. Example: `class Da...

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/services/federation.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/http.ts
  • packages/api/src/api/activitypub-schema.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Forbidden constructs in CORE code: any, eslint-disable, ts-ignore, async/await, raw Promise chains (then/catch), Promise.all, try/catch for logic control, console.*, switch statements (use Match with .exhaustive() instead)
All functions must use Effect-TS for composing effects: Effect<Success, Error, Requirements>. No direct async/await, Promise chains, or try/catch in product logic.
Functional comments must include: CHANGE, WHY, QUOTE(ТЗ) or n/a, REF, SOURCE or n/a, FORMAT THEOREM, PURITY (CORE|SHELL), EFFECT signature for SHELL functions, INVARIANT, and COMPLEXITY.
All data mutations must use immutable patterns (ReadonlyArray, readonly properties, Object.freeze); mutation in SHELL only when absolutely necessary and documented.

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/services/federation.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/http.ts
  • packages/api/src/api/activitypub-schema.ts
**/*.{sh,bash,py,js,ts,jsx,tsx,go,java,rb,php}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce command injection or unsafe shell/process execution with user-controlled input

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/services/federation.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/http.ts
  • packages/api/src/api/activitypub-schema.ts
**/*.{py,js,ts,jsx,tsx,go,java,rb,php,sh,bash,c,cpp}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce path traversal or writes outside intended project/container state directories

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/services/federation.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/http.ts
  • packages/api/src/api/activitypub-schema.ts
**/*.{js,ts,jsx,tsx,py,java,go,rb,php,sh,bash,yml,yaml,json,env*,toml,cfg,config,dockerfile,dockerignore}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files expose credentials, tokens, private-keys, or PII in source, generated config, logs, or CI output

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/services/federation.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/http.ts
  • packages/api/src/api/activitypub-schema.ts
packages/api/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (README.md)

API layer should work only with LIB and serve as the intermediary between APP and LIB

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/services/federation.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/http.ts
  • packages/api/src/api/activitypub-schema.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Ты строгий ревьюер SPEC DRIVEN DEVELOPMENT.

Перед выводами изучи README.md, другие *.md файлы, linked issues,
PR description, PR comments/discussion и релевантную кодовую базу.

Сверь изменения с исходным ТЗ/спекой и обсуждением. Флагай любой уход
от спеки, недокументированное изменение поведения, отсутствие тестов
для заявленного поведения и security-риск. Если спека не видна,
попроси автора добавить ее в issue или PR description.

Проверь решение с точки зрения формальной верификации: какие инварианты,
предусловия и постусловия можно доказать математически, а где доказуемость
слабая. Оцени решение с точки зрения теории игр: устойчивы ли стимулы,
нет ли выгодного обхода правил, и какое решение было бы сильнее.

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/services/federation.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/http.ts
  • packages/api/src/api/activitypub-schema.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Implement property-based testing using fast-check for mathematical properties and invariants. Example: fc.property(fc.array(messageArbitrary), (messages) => isChronologicallySorted(sortMessagesByTimestamp(messages))).
Mock external dependencies in unit tests using Effect's testing utilities. Run tests without Effect runtime for speed. Example: Effect.provide(MockService), Effect.runPromise.

Files:

  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/tests/http-config.test.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Property-based tests (fast-check) must verify mathematical invariants; unit tests must use Effect test utilities without async/await.

Files:

  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/tests/http-config.test.ts
🧠 Learnings (1)
📚 Learning: 2026-05-18T07:43:38.131Z
Learnt from: skulidropek
Repo: ProverCoderAI/docker-git PR: 313
File: packages/api/src/services/panel-cloudflare-tunnel.ts:123-135
Timestamp: 2026-05-18T07:43:38.131Z
Learning: In this repo’s ProverCoderAI/docker-git architecture, it is intentional for service modules under packages/api/src/services/ to call Node.js APIs directly (e.g., fs, child_process, fetch). Do not treat direct Node API usage in these service modules as a dependency-injection (DI) violation. This codebase provides the NodeContext at the program boundary (entry point) rather than injecting Context.Tag/Layer services into individual service modules; reviewers should only flag DI/context issues if the expected boundary setup is missing.

Applied to files:

  • packages/api/src/services/federation.ts
🔇 Additional comments (2)
packages/api/src/api/contracts.ts (1)

573-574: LGTM!

Also applies to: 576-578, 618-627

packages/api/src/api/schema.ts (1)

3-16: LGTM!

Comment thread packages/api/src/api/activitypub-schema.ts Outdated
Comment thread packages/api/src/api/activitypub-schema.ts Outdated
Comment thread packages/api/tests/activitypub-schema-parity.test.ts
Comment thread packages/api/tests/http-config.test.ts
@skulidropek
Copy link
Copy Markdown
Member Author

@coderabbitai можешь проверить на сколько код реализует

{"[@context](https://t.me/context)":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1","https://purl.archive.org/socialweb/webfinger",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","toot":"http://joinmastodon.org/ns#","featured":{"@id":"toot:featured","[@type](https://t.me/type)":"@id"},"featuredTags":{"@id":"toot:featuredTags","[@type](https://t.me/type)":"@id"},"alsoKnownAs":{"@id":"as:alsoKnownAs","[@type](https://t.me/type)":"@id"},"movedTo":{"@id":"as:movedTo","[@type](https://t.me/type)":"@id"},"schema":"http://schema.org/#","PropertyValue":"schema:PropertyValue","value":"schema:value","discoverable":"toot:discoverable","suspended":"toot:suspended","memorial":"toot:memorial","indexable":"toot:indexable","attributionDomains":{"@id":"toot:attributionDomains","[@type](https://t.me/type)":"@id"},"showFeatured":"toot:showFeatured","showMedia":"toot:showMedia","showRepliesInMedia":"toot:showRepliesInMedia","gts":"https://gotosocial.org/ns#","interactionPolicy":{"@id":"gts:interactionPolicy","[@type](https://t.me/type)":"@id"},"canQuote":{"@id":"gts:canQuote","[@type](https://t.me/type)":"@id"},"automaticApproval":{"@id":"gts:automaticApproval","[@type](https://t.me/type)":"@id"},"manualApproval":{"@id":"gts:manualApproval","[@type](https://t.me/type)":"@id"}}],"id":"[https://](https://mastodon.social/users/GordonFreeman)[mastodon.social/users/GordonFreeman","webfinger":"](https://mastodon.social/users/GordonFreeman%22,%22webfinger%22:%22GordonFreeman@mastodon.social%22,%22type%22:%22Person%22,%22following%22:%22https://mastodon.social/users/GordonFreeman/following%22,%22followers%22:%22https://mastodon.social/users/GordonFreeman/followers%22,%22inbox%22:%22https://mastodon.social/users/)[GordonFreeman@mastodon.social](mailto:GordonFreeman@mastodon.social)","type":"Person","following":"[https://mastodon.social/users/GordonFreeman/following","followers":"https://mastodon.social/users/GordonFreeman/followers","inbox":"https://mastodon.social/users/GordonFreeman/inbox","outbox":"https://mastodon.social/users/GordonFreeman/outbox","featured":"https://mastodon.social/users/GordonFreeman/collections/featured","featuredTags":"https://mastodon.social/users/GordonFreeman/collections/tags","preferredUsername":"GordonFreeman","name":"GordonFreeman","summary":"","url":"https://mastodon.social/@GordonFreeman","manuallyApprovesFollowers":false,"discoverable":false,"indexable":false,"published":"2022-05-11T00:00:00Z","memorial":false,"showFeatured":true,"showMedia":true,"showRepliesInMedia":true,"interactionPolicy":{"canFeature":{"automaticApproval":["https://mastodon.social/users/GordonFreeman"]}},"featuredCollections":"https://mastodon.social/ap/users/108283196203417442/featured_collections","publicKey":{"id":"https://mastodon.social/users/GordonFreeman#main-key","owner":"https://mastodon.social/users/GordonFreeman","publicKeyPem":"-----BEGIN](https://mastodon.social/users/GordonFreeman/following%22,%22followers%22:%22https://mastodon.social/users/GordonFreeman/followers%22,%22inbox%22:%22https://mastodon.social/users/GordonFreeman/inbox%22,%22outbox%22:%22https://mastodon.social/users/GordonFreeman/outbox%22,%22featured%22:%22https://mastodon.social/users/GordonFreeman/collections/featured%22,%22featuredTags%22:%22https://mastodon.social/users/GordonFreeman/collections/tags%22,%22preferredUsername%22:%22GordonFreeman%22,%22name%22:%22GordonFreeman%22,%22summary%22:%22%22,%22url%22:%22https://mastodon.social/@GordonFreeman%22,%22manuallyApprovesFollowers%22:false,%22discoverable%22:false,%22indexable%22:false,%22published%22:%222022-05-11T00:00:00Z%22,%22memorial%22:false,%22showFeatured%22:true,%22showMedia%22:true,%22showRepliesInMedia%22:true,%22interactionPolicy%22:%7B%22canFeature%22:%7B%22automaticApproval%22:%5B%22https://mastodon.social/users/GordonFreeman%22%5D%7D%7D,%22featuredCollections%22:%22https://mastodon.social/ap/users/108283196203417442/featured_collections%22,%22publicKey%22:%7B%22id%22:%22https://mastodon.social/users/GordonFreeman#main-key%22,%22owner%22:%22https://mastodon.social/users/GordonFreeman%22,%22publicKeyPem%22:%22-----BEGIN) PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0zWlvp7jHBsNV/3QkBnu\nZpbtPWetfBFJsK7huV3ZLsUk+oeyAPQKO4KCiZiBZm3qVBCr6O7p75GAesXRpMVN\ngkrgzFCI4ZME0x/6VrVNvo4q8xpRxu+rAsULXa1mI9XiUDDcFTIJn8wW09uGa4XK\nW0KGwe2+F6q7OVJfkiAXbwRrjBg6Stn8NOd6n/TYFw4UnyMNZ6BPqnxxhEv1GsFn\n6viF/jrT6SpIY0K6kBlMk5AepFj/5LpoiTdH7bfpja3jbGQNF/h1PJ0U1kP4JvNT\nNOnWMS+5fMovjftxPwwqggB8FrHeDF1QOk8ETznugukqhtNpLTx28P+sEAJC8eZS\nPwIDAQAB\n-----END PUBLIC KEY-----\n"},"tag":[],"attachment":[],"endpoints":{"sharedInbox":"[https://mastodon.social/inbox"}}](https://mastodon.social/inbox%22%7D%7D)

Skuli Dropek, [19.05.2026 22:04]
Вот как пример от mastodon

Skuli Dropek, [19.05.2026 22:06]
Именно follows запроси curl -s -H "Accept: application/activity+json"
"https://mastodon.social/users/nixCraft/followers?page=1"

Skuli Dropek, [19.05.2026 21:43]
https://forgefed.org/spec/
Добавь tpyechecker [@context](https://t.me/context)

Должно быть 1 в 1

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cat packages/api/src/api/activitypub-schema.ts
echo "---CONTRACTS---"
cat packages/api/src/api/contracts.ts

Length of output: 26933


@skulidropek Вот детальный анализ паритета схемы с реальным Mastodon Actor JSON:


✅ Что работает корректно

Реальный Mastodon JSON успешно пройдёт декодирование через ActivityPubPersonSchema, потому что все обязательные поля присутствуют:

  • @context, type: "Person", id, name, preferredUsername, summary, inbox, outbox, followers, following

Опциональные поля webfinger, featured, featuredTags, url, manuallyApprovesFollowers, discoverable, indexable, published, memorial, showFeatured, showMedia, showRepliesInMedia, interactionPolicy, featuredCollections, publicKey, tag, attachment, endpointsвсе есть в схеме.


❌ Пробелы (не покрывает реальный Mastodon JSON)

1. @context — нет структурной валидации

ActivityPubPersonSchema использует JsonLdContextSchema (принимает любой string/object/array). Реальный Mastodon @context — это кортеж из 4 элементов:

[
  "https://www.w3.org/ns/activitystreams",
  "https://w3id.org/security/v1",
  "https://purl.archive.org/socialweb/webfinger",   ← отсутствует в ActorJsonLdContextSchema
  { manuallyApprovesFollowers, toot, featured, featuredTags, alsoKnownAs, movedTo,
    schema, PropertyValue, value, discoverable, suspended, memorial, indexable,
    attributionDomains, showFeatured, showMedia, showRepliesInMedia,
    gts, interactionPolicy, canQuote, automaticApproval, manualApproval }  ← не типизирован
]

ActorJsonLdContextSchema жёстко задаёт [activityStreams, security, forgeFed] — т.е. использует https://forgefed.org/ns вместо https://purl.archive.org/socialweb/webfinger. Для Mastodon-акторов это неправильно.

Нужна примерно такая схема для контекста Mastodon-актора:

const MastodonActorContextExtensionsSchema = Schema.Struct({
  manuallyApprovesFollowers: Schema.optional(Schema.String),
  toot: Schema.optional(Schema.String),
  featured: Schema.optional(Schema.Struct({ "`@id`": Schema.String, "`@type`": Schema.String })),
  featuredTags: Schema.optional(Schema.Struct({ "`@id`": Schema.String, "`@type`": Schema.String })),
  alsoKnownAs: Schema.optional(Schema.Struct({ "`@id`": Schema.String, "`@type`": Schema.String })),
  movedTo: Schema.optional(Schema.Struct({ "`@id`": Schema.String, "`@type`": Schema.String })),
  discoverable: Schema.optional(Schema.String),
  suspended: Schema.optional(Schema.String),
  memorial: Schema.optional(Schema.String),
  indexable: Schema.optional(Schema.String),
  // gts extensions
  gts: Schema.optional(Schema.String),
  interactionPolicy: Schema.optional(Schema.Struct({ "`@id`": Schema.String, "`@type`": Schema.String })),
  // PropertyValue
  schema: Schema.optional(Schema.String),
  PropertyValue: Schema.optional(Schema.String),
  value: Schema.optional(Schema.String),
}).pipe(Schema.extend(Schema.Record({ key: Schema.String, value: JsonValueSchema })))

export const MastodonActorJsonLdContextSchema = Schema.Tuple(
  Schema.Literal("https://www.w3.org/ns/activitystreams"),
  Schema.Literal("https://w3id.org/security/v1"),
  Schema.Literal("https://purl.archive.org/socialweb/webfinger"),
  MastodonActorContextExtensionsSchema
)

2. Отсутствующие опциональные поля Person

Поля, которые могут присутствовать у реальных Mastodon-акторов, но отсутствуют в схеме (при декодировании они будут молча удалены):

Поле Тип Описание
alsoKnownAs string[] Псевдонимы аккаунта (при переезде)
movedTo string Ссылка на новый аккаунт
suspended boolean Заблокирован ли аккаунт
attributionDomains string[] Домены для attribution
icon object Аватар (Image с url, mediaType)
image object Шапка профиля
devices string URL списка устройств

3. tag и attachment — не типизированы

Сейчас это Schema.Array(Schema.Unknown). В реальных ответах:

  • attachment — массив PropertyValue объектов ({ type: "PropertyValue", name: string, value: string })
  • tag — массив Hashtag / Emoji объектов

4. interactionPolicy — слишком широкая схема

Текущая: Schema.Record({ key: Schema.String, value: JsonValueSchema }).

Реальная структура:

{
  "canFeature": { "automaticApproval": ["..."] },
  "canQuote": { "automaticApproval": [...], "manualApproval": [...] }
}

5. Поле name — обязательное, но может отсутствовать

У сервисных аккаунтов/ботов name может отсутствовать. Рекомендуется сделать OptionalString.


Итоговая оценка

Аспект Статус
Все обязательные поля Person ✅ Покрыто
Опциональные поля Mastodon ✅ ~85% покрыто
@context type-check ❌ Не реализован
alsoKnownAs, movedTo, suspended ❌ Отсутствуют
attachment / tag типизация ⚠️ Loose (Unknown)
interactionPolicy структура ⚠️ Loose (Record)
icon / image / devices ❌ Отсутствуют

Основной запрос — добавить строгую схему @context — требует создания MastodonActorJsonLdContextSchema (или обобщённого GenericActorJsonLdContextSchema), который принимает webfinger URI и типизированный объект расширений. Хотите, чтобы я запланировал эти изменения?

Guarantee schema parity with the Mastodon actor fixture and remove Schema.Unknown from the ActivityPub API boundary.

Proof obligations: required ActivityPub object fields decode iff their literal type/context invariants hold; ordered collection items and ForgeFed raw payloads are JSON values; unsupported followers pages fail with typed 400 errors.
@skulidropek
Copy link
Copy Markdown
Member Author

skulidropek commented May 21, 2026

AI Session Backup

Commit: 663c0e1
Status: success
Files: 4 (2.34 MB)
Links: README | Manifest

git status

On branch issue-334
Your branch is up to date with 'origin/issue-334'.

nothing to commit, working tree clean

@skulidropek
Copy link
Copy Markdown
Member Author

@coderabbitai please verify whether issue #334 is now fully implemented against the issue JSON and your review comments. In particular, please check the proof obligations in the PR body: exact actor JSON-LD context parity, no Schema.Unknown in ActivityPub/ForgeFed public schemas, Mastodon actor optional fields, structured tag/attachment, typed interactionPolicy, property-based schema invariants, and the negative /federation/followers?page=2 HTTP test. If any proof gap remains, list the exact remaining blocker.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== activitypub-schema.ts ==="
cat packages/api/src/api/activitypub-schema.ts

echo ""
echo "=== http.ts (federation routes) ==="
cat packages/api/src/http.ts

echo ""
echo "=== activitypub-schema-parity.test.ts ==="
cat packages/api/tests/activitypub-schema-parity.test.ts

echo ""
echo "=== http-config.test.ts ==="
cat packages/api/tests/http-config.test.ts

echo ""
echo "=== federation.ts ==="
cat packages/api/src/services/federation.ts

echo ""
echo "=== Issue `#334` body ==="
gh issue view 334 --repo ProverCoderAI/docker-git

Length of output: 178670


@skulidropek — после полной проверки всех файлов (activitypub-schema.ts, http.ts, federation.ts, оба тестовых файла), все 7 proof obligations из PR закрыты. Подробный отчёт:


✅ Все требования реализованы — блокеров нет

1. Exact actor JSON-LD context parity ✅

MastodonActorJsonLdContextSchema — точный 4-элементный Tuple:

[activityStreams, security, socialWebWebfinger, MastodonActorContextExtensionsSchema]

MastodonActorContextExtensionsSchema покрывает все ключи из issue-JSON: manuallyApprovesFollowers, toot, featured, featuredTags, alsoKnownAs, movedTo, schema, PropertyValue, value, discoverable, suspended, memorial, indexable, attributionDomains, showFeatured, showMedia, showRepliesInMedia, gts, interactionPolicy, canQuote, automaticApproval, manualApproval.

ActorJsonLdContextSchema = Union(LocalActorJsonLdContextSchema, MastodonActorJsonLdContextSchema) покрывает оба варианта. Тест decodes a Mastodon actor Person fixture прогоняет полный mastodonActorFixture.


2. No Schema.Unknown in ActivityPub/ForgeFed public schemas ✅

Поле Тип
tag Schema.Array(ActivityPubActorTagSchema)
attachment Schema.Array(ActivityPubActorAttachmentSchema)
orderedItems Schema.Array(JsonValueSchema)
ForgeFedTicketSchema.attachment Schema.Array(JsonValueSchema)
ForgeFedTicketSchema.raw Schema.optional(JsonValueSchema)

Schema.Unknown не встречается ни в одном экспортируемом ActivityPub/ForgeFed схеме.


3. Mastodon actor optional fields ✅

Все поля, ранее отсутствовавшие, теперь есть в ActivityPubPersonSchema:
alsoKnownAs, movedTo, suspended, attributionDomains, icon, image, devices, showFeatured, showMedia, showRepliesInMedia, featuredCollections, memorial, name (сделан optional).


4. Structured tag/attachment

ActivityPubActorTagSchema = Struct({ type, name, href, id, icon, updated }, Record(String, JsonValue))
ActivityPubActorAttachmentSchema = Struct({ type, name, value }, Record(String, JsonValue))

Оба — типизированные Struct с открытым Record для forward-compat.


5. Typed interactionPolicy

MastodonInteractionPolicySchema = Struct({
  canFeature: optional(ActivityPubInteractionApprovalSchema),
  canQuote:   optional(ActivityPubInteractionApprovalSchema)
}, Record(String, JsonValue))

ActivityPubInteractionApprovalSchema = Struct({
  automaticApproval: optional(Array(String)),
  manualApproval:    optional(Array(String))
}, Record(String, JsonValue))

Fixture interactionPolicy.canFeature.automaticApproval: [...] декодируется корректно.


6. Property-based schema invariants ✅

В activitypub-schema-parity.test.ts присутствуют fc.property тесты для всех трёх схем:

  • Person: принимает с обязательными полями, отклоняет с неверным type, отклоняет с отсутствующими полями
  • OrderedCollection: аналогично
  • OrderedCollectionPage: аналогично

Итого 9 property-based тест-кейсов.


7. Negative /federation/followers?page=2 HTTP test ✅

В http-config.test.ts:

it.effect("rejects unsupported followers pages", () => ...)
// expect(document.status).toBe(400)
// expect(...error.type).toBe("ApiBadRequestError")
// expect(...error.message).toBe("Unsupported followers page: 2")

readFollowersPageMode в http.ts возвращает Effect.fail(new ApiBadRequestError(...)) для любого page !== "1".


PR #335 полностью реализует все требования issue #334. Нет ни одного оставшегося блокера.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/api/src/api/activitypub-schema.ts (1)

119-122: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Слишком широкий fallback в interactionPolicy ослабляет parity-доказательство.

Сейчас interactionPolicy на Line 189 принимает почти любой объект из-за ActivityPubInteractionPolicySchema (Record<string, JsonValue>). Это пропускает структурно неверные payload и не выполняет требование “fail on mismatched literals”.

Возможный минимальный фикс
-const ActivityPubInteractionPolicySchema = Schema.Record({
-  key: Schema.String,
-  value: JsonValueSchema
-})
@@
-  interactionPolicy: Schema.optional(Schema.Union(MastodonInteractionPolicySchema, ActivityPubInteractionPolicySchema)),
+  interactionPolicy: Schema.optional(MastodonInteractionPolicySchema),

Also applies to: 189-189

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/api/src/api/activitypub-schema.ts` around lines 119 - 122, The
interactionPolicy acceptance is too permissive because
ActivityPubInteractionPolicySchema currently allows arbitrary object shapes;
tighten it so mismatched literals fail by replacing the loose Record schema with
a dictionary mapping of string keys to JsonValue (e.g., use
Schema.Dict(Schema.String, JsonValueSchema) or, if you know allowed keys, a
Schema.Record with explicit literal keys) and update the interactionPolicy usage
to expect that stricter ActivityPubInteractionPolicySchema; change the
ActivityPubInteractionPolicySchema definition (and any code referencing
interactionPolicy) to the narrower schema so structurally invalid payloads are
rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/api/src/api/activitypub-schema.ts`:
- Around line 124-145: The nested actor schemas (ActivityPubImageSchema,
ActivityPubActorAttachmentSchema, ActivityPubActorTagSchema) are too permissive
because they allow all fields optional and an open record of extra keys, so
icon/image/tag/attachment decode succeeds without required structure; tighten
them by making "type" required (use Schema.String not OptionalString) and
replace the broad Schema.Record extra-keys allowance with explicit discriminated
unions or concrete schemas for expected types (e.g., PropertyValue, Emoji,
Hashtag or an explicit Image/Attachment/Tag schema) so each variant enforces its
required fields; update ActivityPubImageSchema,
ActivityPubActorAttachmentSchema, ActivityPubActorTagSchema accordingly (and
apply same tightening to the other occurrences mentioned at the same spots).

---

Outside diff comments:
In `@packages/api/src/api/activitypub-schema.ts`:
- Around line 119-122: The interactionPolicy acceptance is too permissive
because ActivityPubInteractionPolicySchema currently allows arbitrary object
shapes; tighten it so mismatched literals fail by replacing the loose Record
schema with a dictionary mapping of string keys to JsonValue (e.g., use
Schema.Dict(Schema.String, JsonValueSchema) or, if you know allowed keys, a
Schema.Record with explicit literal keys) and update the interactionPolicy usage
to expect that stricter ActivityPubInteractionPolicySchema; change the
ActivityPubInteractionPolicySchema definition (and any code referencing
interactionPolicy) to the narrower schema so structurally invalid payloads are
rejected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0734e014-5015-4a05-a93f-338cba998630

📥 Commits

Reviewing files that changed from the base of the PR and between 1700485 and 663c0e1.

📒 Files selected for processing (5)
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/api/contracts.ts
  • packages/api/src/services/federation.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/tests/http-config.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: E2E (Runtime volumes + SSH)
  • GitHub Check: Test
  • GitHub Check: E2E (Clone auto-open SSH)
  • GitHub Check: E2E (Clone cache)
  • GitHub Check: E2E (Browser command)
  • GitHub Check: E2E (Login context)
  • GitHub Check: E2E (OpenCode)
  • GitHub Check: Lint
  • GitHub Check: Final build (windows-latest)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Implement Functional Core, Imperative Shell (FCIS) pattern: CORE layer contains only pure functions with immutable data and mathematical operations; SHELL layer isolates all effects (IO, network, database). Strict dependency direction: SHELL → CORE (never reverse).
Never use any, unknown, eslint-disable, ts-ignore, or as type assertions (except in rigorously justified cases with documentation). Always use exhaustive union type analysis through .exhaustive() pattern matching.
All external dependencies must be wrapped through typed interfaces and injected via Effect-TS Layer pattern. Never call external services directly from CORE functions.
Use monadic composition with Effect-TS for all effects: Effect<Success, Error, Requirements>. Compose effects through pipe() and Effect.flatMap(). Implement dependency injection via Layer pattern. Handle errors without try/catch blocks.
All functions must be pure in the CORE layer: no side effects (logging, console output, IO operations, mutations). Separate all side effects into the SHELL layer.
Use exhaustive pattern matching with Effect.Match instead of switch statements. Example: Match.value(item).pipe(Match.when(...), Match.exhaustive).
Document all functions with comprehensive TSDoc including: @pure (true/false), @effect (required services), @invariant (mathematical invariants), @precondition, @postcondition, @complexity (time and space), @throws Never (errors must be typed in Effect).
Use functional comment markers for code clarity: CHANGE (brief description), WHY (mathematical/architectural justification), QUOTE(ТЗ) (requirement citation), REF (RTM or message ID), SOURCE (external source with quote), FORMAT THEOREM (∀x ∈ Domain: P(x) → Q(f(x))), PURITY (CORE|SHELL), EFFECT (Effect type signature), INVARIANT (mathematical invariant), COMPLEXITY (time/space).
Define all external service dependencies as Context.Tag classes with fully typed methods returning Effect types. Example: `class Da...

Files:

  • packages/api/tests/http-config.test.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/api/contracts.ts
  • packages/api/src/services/federation.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Implement property-based testing using fast-check for mathematical properties and invariants. Example: fc.property(fc.array(messageArbitrary), (messages) => isChronologicallySorted(sortMessagesByTimestamp(messages))).
Mock external dependencies in unit tests using Effect's testing utilities. Run tests without Effect runtime for speed. Example: Effect.provide(MockService), Effect.runPromise.

Files:

  • packages/api/tests/http-config.test.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Forbidden constructs in CORE code: any, eslint-disable, ts-ignore, async/await, raw Promise chains (then/catch), Promise.all, try/catch for logic control, console.*, switch statements (use Match with .exhaustive() instead)
All functions must use Effect-TS for composing effects: Effect<Success, Error, Requirements>. No direct async/await, Promise chains, or try/catch in product logic.
Functional comments must include: CHANGE, WHY, QUOTE(ТЗ) or n/a, REF, SOURCE or n/a, FORMAT THEOREM, PURITY (CORE|SHELL), EFFECT signature for SHELL functions, INVARIANT, and COMPLEXITY.
All data mutations must use immutable patterns (ReadonlyArray, readonly properties, Object.freeze); mutation in SHELL only when absolutely necessary and documented.

Files:

  • packages/api/tests/http-config.test.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/api/contracts.ts
  • packages/api/src/services/federation.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Property-based tests (fast-check) must verify mathematical invariants; unit tests must use Effect test utilities without async/await.

Files:

  • packages/api/tests/http-config.test.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
**/*.{sh,bash,py,js,ts,jsx,tsx,go,java,rb,php}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce command injection or unsafe shell/process execution with user-controlled input

Files:

  • packages/api/tests/http-config.test.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/api/contracts.ts
  • packages/api/src/services/federation.ts
**/*.{py,js,ts,jsx,tsx,go,java,rb,php,sh,bash,c,cpp}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce path traversal or writes outside intended project/container state directories

Files:

  • packages/api/tests/http-config.test.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/api/contracts.ts
  • packages/api/src/services/federation.ts
**/*.{js,ts,jsx,tsx,py,java,go,rb,php,sh,bash,yml,yaml,json,env*,toml,cfg,config,dockerfile,dockerignore}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files expose credentials, tokens, private-keys, or PII in source, generated config, logs, or CI output

Files:

  • packages/api/tests/http-config.test.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/api/contracts.ts
  • packages/api/src/services/federation.ts
packages/api/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (README.md)

API layer should work only with LIB and serve as the intermediary between APP and LIB

Files:

  • packages/api/tests/http-config.test.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/api/contracts.ts
  • packages/api/src/services/federation.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Ты строгий ревьюер SPEC DRIVEN DEVELOPMENT.

Перед выводами изучи README.md, другие *.md файлы, linked issues,
PR description, PR comments/discussion и релевантную кодовую базу.

Сверь изменения с исходным ТЗ/спекой и обсуждением. Флагай любой уход
от спеки, недокументированное изменение поведения, отсутствие тестов
для заявленного поведения и security-риск. Если спека не видна,
попроси автора добавить ее в issue или PR description.

Проверь решение с точки зрения формальной верификации: какие инварианты,
предусловия и постусловия можно доказать математически, а где доказуемость
слабая. Оцени решение с точки зрения теории игр: устойчивы ли стимулы,
нет ли выгодного обхода правил, и какое решение было бы сильнее.

Files:

  • packages/api/tests/http-config.test.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/api/contracts.ts
  • packages/api/src/services/federation.ts
🧠 Learnings (1)
📚 Learning: 2026-05-18T07:43:38.131Z
Learnt from: skulidropek
Repo: ProverCoderAI/docker-git PR: 313
File: packages/api/src/services/panel-cloudflare-tunnel.ts:123-135
Timestamp: 2026-05-18T07:43:38.131Z
Learning: In this repo’s ProverCoderAI/docker-git architecture, it is intentional for service modules under packages/api/src/services/ to call Node.js APIs directly (e.g., fs, child_process, fetch). Do not treat direct Node API usage in these service modules as a dependency-injection (DI) violation. This codebase provides the NodeContext at the program boundary (entry point) rather than injecting Context.Tag/Layer services into individual service modules; reviewers should only flag DI/context issues if the expected boundary setup is missing.

Applied to files:

  • packages/api/src/services/federation.ts
🔇 Additional comments (4)
packages/api/src/api/contracts.ts (1)

559-559: LGTM!

Also applies to: 574-627

packages/api/src/services/federation.ts (1)

13-13: LGTM!

Also applies to: 130-147, 620-629, 653-653, 670-670, 674-683, 725-728, 750-750, 765-765

packages/api/tests/activitypub-schema-parity.test.ts (1)

3-3: LGTM!

Also applies to: 15-83, 304-419

packages/api/tests/http-config.test.ts (1)

135-138: LGTM!

Also applies to: 289-299

Comment thread packages/api/src/api/activitypub-schema.ts
@skulidropek
Copy link
Copy Markdown
Member Author

https://github.com/coderabbitai можешь проверить на сколько код реализует

{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1","https://purl.archive.org/socialweb/webfinger",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","toot":"http://joinmastodon.org/ns#","featured":{"@id":"toot:featured","@type":"@id"},"featuredTags":{"@id":"toot:featuredTags","@type":"@id"},"alsoKnownAs":{"@id":"as:alsoKnownAs","@type":"@id"},"movedTo":{"@id":"as:movedTo","@type":"@id"},"schema":"http://schema.org/#","PropertyValue":"schema:PropertyValue","value":"schema:value","discoverable":"toot:discoverable","suspended":"toot:suspended","memorial":"toot:memorial","indexable":"toot:indexable","attributionDomains":{"@id":"toot:attributionDomains","@type":"@id"},"showFeatured":"toot:showFeatured","showMedia":"toot:showMedia","showRepliesInMedia":"toot:showRepliesInMedia","gts":"https://gotosocial.org/ns#","interactionPolicy":{"@id":"gts:interactionPolicy","@type":"@id"},"canQuote":{"@id":"gts:canQuote","@type":"@id"},"automaticApproval":{"@id":"gts:automaticApproval","@type":"@id"},"manualApproval":{"@id":"gts:manualApproval","@type":"@id"}}],"id":"https://mastodon.social/users/GordonFreeman","webfinger":"GordonFreeman@mastodon.social","type":"Person","following":"https://mastodon.social/users/GordonFreeman/following","followers":"https://mastodon.social/users/GordonFreeman/followers","inbox":"https://mastodon.social/users/GordonFreeman/inbox","outbox":"https://mastodon.social/users/GordonFreeman/outbox","featured":"https://mastodon.social/users/GordonFreeman/collections/featured","featuredTags":"https://mastodon.social/users/GordonFreeman/collections/tags","preferredUsername":"GordonFreeman","name":"GordonFreeman","summary":"","url":"https://mastodon.social/@GordonFreeman","manuallyApprovesFollowers":false,"discoverable":false,"indexable":false,"published":"2022-05-11T00:00:00Z","memorial":false,"showFeatured":true,"showMedia":true,"showRepliesInMedia":true,"interactionPolicy":{"canFeature":{"automaticApproval":["https://mastodon.social/users/GordonFreeman"]}},"featuredCollections":"https://mastodon.social/ap/users/108283196203417442/featured_collections","publicKey":{"id":"https://mastodon.social/users/GordonFreeman#main-key","owner":"https://mastodon.social/users/GordonFreeman","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0zWlvp7jHBsNV/3QkBnu\nZpbtPWetfBFJsK7huV3ZLsUk+oeyAPQKO4KCiZiBZm3qVBCr6O7p75GAesXRpMVN\ngkrgzFCI4ZME0x/6VrVNvo4q8xpRxu+rAsULXa1mI9XiUDDcFTIJn8wW09uGa4XK\nW0KGwe2+F6q7OVJfkiAXbwRrjBg6Stn8NOd6n/TYFw4UnyMNZ6BPqnxxhEv1GsFn\n6viF/jrT6SpIY0K6kBlMk5AepFj/5LpoiTdH7bfpja3jbGQNF/h1PJ0U1kP4JvNT\nNOnWMS+5fMovjftxPwwqggB8FrHeDF1QOk8ETznugukqhtNpLTx28P+sEAJC8eZS\nPwIDAQAB\n-----END PUBLIC KEY-----\n"},"tag":[],"attachment":[],"endpoints":{"sharedInbox":"https://mastodon.social/inbox"}}

Skuli Dropek, [19.05.2026 22:04]
Вот как пример от mastodon

Skuli Dropek, [19.05.2026 22:06]
Именно follows запроси curl -s -H "Accept: application/activity+json"
"https://mastodon.social/users/nixCraft/followers?page=1"

Skuli Dropek, [19.05.2026 21:43]
https://forgefed.org/spec/
Добавь tpyechecker @context
Должно быть 1 в 1

@skulidropek
Copy link
Copy Markdown
Member Author

@coderabbitai можешь проверить на сколько код реализует

{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1","https://purl.archive.org/socialweb/webfinger",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","toot":"http://joinmastodon.org/ns#","featured":{"@id":"toot:featured","@type":"@id"},"featuredTags":{"@id":"toot:featuredTags","@type":"@id"},"alsoKnownAs":{"@id":"as:alsoKnownAs","@type":"@id"},"movedTo":{"@id":"as:movedTo","@type":"@id"},"schema":"http://schema.org/#","PropertyValue":"schema:PropertyValue","value":"schema:value","discoverable":"toot:discoverable","suspended":"toot:suspended","memorial":"toot:memorial","indexable":"toot:indexable","attributionDomains":{"@id":"toot:attributionDomains","@type":"@id"},"showFeatured":"toot:showFeatured","showMedia":"toot:showMedia","showRepliesInMedia":"toot:showRepliesInMedia","gts":"https://gotosocial.org/ns#","interactionPolicy":{"@id":"gts:interactionPolicy","@type":"@id"},"canQuote":{"@id":"gts:canQuote","@type":"@id"},"automaticApproval":{"@id":"gts:automaticApproval","@type":"@id"},"manualApproval":{"@id":"gts:manualApproval","@type":"@id"}}],"id":"https://mastodon.social/users/GordonFreeman","webfinger":"GordonFreeman@mastodon.social","type":"Person","following":"https://mastodon.social/users/GordonFreeman/following","followers":"https://mastodon.social/users/GordonFreeman/followers","inbox":"https://mastodon.social/users/GordonFreeman/inbox","outbox":"https://mastodon.social/users/GordonFreeman/outbox","featured":"https://mastodon.social/users/GordonFreeman/collections/featured","featuredTags":"https://mastodon.social/users/GordonFreeman/collections/tags","preferredUsername":"GordonFreeman","name":"GordonFreeman","summary":"","url":"https://mastodon.social/@GordonFreeman","manuallyApprovesFollowers":false,"discoverable":false,"indexable":false,"published":"2022-05-11T00:00:00Z","memorial":false,"showFeatured":true,"showMedia":true,"showRepliesInMedia":true,"interactionPolicy":{"canFeature":{"automaticApproval":["https://mastodon.social/users/GordonFreeman"]}},"featuredCollections":"https://mastodon.social/ap/users/108283196203417442/featured_collections","publicKey":{"id":"https://mastodon.social/users/GordonFreeman#main-key","owner":"https://mastodon.social/users/GordonFreeman","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0zWlvp7jHBsNV/3QkBnu\nZpbtPWetfBFJsK7huV3ZLsUk+oeyAPQKO4KCiZiBZm3qVBCr6O7p75GAesXRpMVN\ngkrgzFCI4ZME0x/6VrVNvo4q8xpRxu+rAsULXa1mI9XiUDDcFTIJn8wW09uGa4XK\nW0KGwe2+F6q7OVJfkiAXbwRrjBg6Stn8NOd6n/TYFw4UnyMNZ6BPqnxxhEv1GsFn\n6viF/jrT6SpIY0K6kBlMk5AepFj/5LpoiTdH7bfpja3jbGQNF/h1PJ0U1kP4JvNT\nNOnWMS+5fMovjftxPwwqggB8FrHeDF1QOk8ETznugukqhtNpLTx28P+sEAJC8eZS\nPwIDAQAB\n-----END PUBLIC KEY-----\n"},"tag":[],"attachment":[],"endpoints":{"sharedInbox":"https://mastodon.social/inbox"}}

Skuli Dropek, [19.05.2026 22:04]
Вот как пример от mastodon

Skuli Dropek, [19.05.2026 22:06]
Именно follows запроси curl -s -H "Accept: application/activity+json"
"https://mastodon.social/users/nixCraft/followers?page=1"

Skuli Dropek, [19.05.2026 21:43]
https://forgefed.org/spec/
Добавь tpyechecker @context
Должно быть 1 в 1

Close the CodeRabbit proof gap by replacing permissive actor extension records with literal discriminated schemas for Image, PropertyValue, Hashtag, and Emoji.

The interactionPolicy contract now accepts only Mastodon canFeature/canQuote approval blocks with at least one approval list, and regression tests reject the prior empty/arbitrary payload counterexamples.
@skulidropek
Copy link
Copy Markdown
Member Author

skulidropek commented May 21, 2026

AI Session Backup

Commit: 5d88686
Status: success
Files: 4 (2.84 MB)
Links: README | Manifest

git status

On branch issue-334
Your branch is up to date with 'origin/issue-334'.

nothing to commit, working tree clean

@skulidropek
Copy link
Copy Markdown
Member Author

@coderabbitai please verify whether pull request review 4334448958 is now fully implemented. I addressed the two proof gaps by replacing permissive actor extension schemas with literal variants (Image, PropertyValue, Hashtag, Emoji), removing the arbitrary interactionPolicy fallback, requiring non-empty Mastodon approval blocks, and adding regression tests for the exact counterexamples. Please confirm whether issue #334 and this review are fully satisfied, or list any remaining blocker.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== activitypub-schema.ts ==="
cat packages/api/src/api/activitypub-schema.ts

echo ""
echo "=== activitypub-schema-parity.test.ts ==="
cat packages/api/tests/activitypub-schema-parity.test.ts

echo ""
echo "=== http-config.test.ts (followers section) ==="
grep -n -A 30 "followers" packages/api/tests/http-config.test.ts | head -120

echo ""
echo "=== PR review comment 4334448958 ==="
gh api repos/ProverCoderAI/docker-git/pulls/335/reviews/4334448958 2>/dev/null || echo "Review not found via reviews endpoint"

echo ""
echo "=== All PR review comments ==="
gh api repos/ProverCoderAI/docker-git/pulls/335/reviews --paginate 2>/dev/null | python3 -c "
import json, sys
reviews = json.load(sys.stdin)
for r in reviews:
    print(f'Review ID: {r[\"id\"]}')
    print(f'State: {r[\"state\"]}')
    print(f'Body (first 500): {r[\"body\"][:500] if r[\"body\"] else \"\"}')
    print('---')
"

echo ""
echo "=== PR inline review comments ==="
gh api repos/ProverCoderAI/docker-git/pulls/335/comments --paginate 2>/dev/null | python3 -c "
import json, sys
comments = json.load(sys.stdin)
for c in comments:
    print(f'Comment ID: {c[\"id\"]}')
    print(f'File: {c[\"path\"]}')
    print(f'Body (first 300): {c[\"body\"][:300]}')
    print('---')
" | head -200

Length of output: 48901


@skulidropek — both proof gaps from review 4334448958 are fully addressed. Here is the verification:


✅ Gap 1 — Literal variants for Image, PropertyValue, Hashtag, Emoji

Schema Required fields enforced Literal type
ActivityPubImageSchema type, url "Image"
ActivityPubActorAttachmentSchema type, name, value "PropertyValue"
ActivityPubHashtagTagSchema type, name, href "Hashtag"
ActivityPubEmojiTagSchema type, id, name, icon "Emoji"
ActivityPubActorTagSchema Discriminated Union(Hashtag, Emoji)

✅ Gap 2 — interactionPolicy fallback removed, non-empty blocks required

  • ActivityPubInteractionApprovalSchema uses Schema.filterrejects unless automaticApproval or manualApproval is present.
  • MastodonInteractionPolicySchema uses Schema.filterrejects unless canFeature or canQuote is present.
  • ActivityPubPersonSchema.interactionPolicy is now Schema.optional(MastodonInteractionPolicySchema) with no loose Record fallback.

✅ Regression tests for exact counterexamples

The test "rejects structurally invalid ActivityPub Person actor extensions" covers all 9 counterexamples:

  • icon: {}, image: { type: "Image" } (no url) → rejected
  • tag: [{}], tag: [Emoji without icon] → rejected
  • attachment: [{}], attachment: [{ type: "Note", ... }] → rejected
  • interactionPolicy: {}, interactionPolicy: { arbitrary: true }, interactionPolicy: { canFeature: { arbitrary: true } } → rejected

Final status

Check Status
Schema.Unknown eliminated
@context actor-specific (ActorJsonLdContextSchema)
Mastodon optional fields (alsoKnownAs, icon, image, etc.)
Literal variants for Image/PropertyValue/Hashtag/Emoji
interactionPolicy fallback removed, non-empty filter
Property-based schema invariant tests (fast-check, 9 × 3)
Negative /federation/followers?page=2 HTTP test (400)
Mastodon actor fixture parity test

PR #335 fully satisfies issue #334 and review 4334448958. No remaining blockers.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== activitypub-schema.ts ==="
cat packages/api/src/api/activitypub-schema.ts

Length of output: 7667


🏁 Script executed:

#!/bin/bash
echo "=== Live followers?page=1 for nixCraft ==="
curl -s -H "Accept: application/activity+json" "https://mastodon.social/users/nixCraft/followers?page=1" | python3 -m json.tool 2>/dev/null || echo "RAW:" && curl -s -H "Accept: application/activity+json" "https://mastodon.social/users/nixCraft/followers?page=1"

Length of output: 2092


@skulidropek — вот детальный построчный анализ паритета текущего кода с предоставленным Mastodon JSON:


@context — 1 в 1

MastodonActorJsonLdContextSchema — точный 4-элементный Tuple:

["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1",
 "https://purl.archive.org/socialweb/webfinger", MastodonActorContextExtensionsSchema]

MastodonActorContextExtensionsSchema покрывает все 22 ключа из объекта расширений в вашей фикстуре:

Ключ фикстуры Тип в фикстуре Схема Статус
manuallyApprovesFollowers "as:..." (string) OptionalString
toot "http://..." (string) OptionalString
featured {"@id":..., "@type":...} JsonLdIdMappingSchema
featuredTags {"@id":..., "@type":...} JsonLdIdMappingSchema
alsoKnownAs {"@id":..., "@type":...} JsonLdIdMappingSchema
movedTo {"@id":..., "@type":...} JsonLdIdMappingSchema
schema "http://schema.org/#" OptionalString
PropertyValue "schema:PropertyValue" OptionalString
value "schema:value" OptionalString
discoverable / suspended / memorial / indexable strings OptionalString
attributionDomains {"@id":..., "@type":...} JsonLdIdMappingSchema
showFeatured / showMedia / showRepliesInMedia strings OptionalString
gts "https://gotosocial.org/ns#" OptionalString
interactionPolicy / canQuote / automaticApproval / manualApproval {"@id":..., "@type":...} JsonLdIdMappingSchema

ActivityPubPersonSchema — все поля фикстуры покрыты

Поле фикстуры Тип в схеме Статус
id, preferredUsername, summary, inbox, outbox, followers, following required String
name OptionalString
webfinger, url, featured, featuredTags, published, featuredCollections OptionalString
manuallyApprovesFollowers, discoverable, indexable, memorial, showFeatured, showMedia, showRepliesInMedia OptionalBoolean
interactionPolicy MastodonInteractionPolicySchema
publicKey ActivityPubPublicKeySchema
tag Array(Union(Hashtag, Emoji))
attachment Array(PropertyValue)
endpoints.sharedInbox ActivityPubEndpointsSchema

followers?page=1 — живой ответ Mastodon

Реальный ответ от https://mastodon.social/users/nixCraft/followers?page=1:

{
  "`@context`": "https://www.w3.org/ns/activitystreams",
  "type": "OrderedCollectionPage",
  "totalItems": 115147,
  "next": "https://mastodon.social/users/nixCraft/followers?page=2",
  "partOf": "https://mastodon.social/users/nixCraft/followers",
  "orderedItems": ["https://mastodon.social/ap/users/...", ...]
}

ActivityPubOrderedCollectionPageSchema покрывает его 1 в 1:

  • @context — строка → JsonLdContextSchema = Union(String, ...)
  • type: "OrderedCollectionPage"
  • totalItems, partOf, id, next (optional) ✅
  • orderedItems: string[] — строки валидны как JsonValue

⚠️ Два мелких нюанса

  1. tag и attachment — закрытые Struct-ы: ActivityPubActorAttachmentSchema не расширяется открытым Record. Если реальный PropertyValue из Mastodon содержит дополнительные поля (редко, но возможно), они будут молча удалены при декодировании. Если нужна полная forward-compat — добавить , Schema.Record({ key: Schema.String, value: JsonValueSchema }) к каждому.

  2. interactionPolicy filter: MastodonInteractionPolicySchema требует наличия хотя бы canFeature или canQuote. Если актор вернёт пустой объект {} в поле interactionPolicy — декодирование упадёт. Учитывая, что фикстура имеет canFeature, это ок, но стоит знать об этом edge case.


Итог: код реализует Mastodon actor JSON и followers?page=1 на ~100% — все поля, включая все 22 ключа @context, покрыты корректными типами.

@skulidropek
Copy link
Copy Markdown
Member Author

skulidropek commented May 21, 2026

AI Session Backup

Commit: a5bcc9b
Status: success
Files: 5 (4.77 MB)
Links: README | Manifest

git status

On branch issue-334
Your branch is up to date with 'origin/issue-334'.

nothing to commit, working tree clean

@skulidropek
Copy link
Copy Markdown
Member Author

@coderabbitai please verify whether issue #334 and review 4334448958 are now fully implemented under exact 1:1 structural parity. I changed the contract from a permissive/superset model to exact schemas plus strict decoding: HTTP and parity tests use exactActivityPubParseOptions with onExcessProperty=error; ActivityPubPersonSchema is now exact local actor | exact Mastodon issue actor; Mastodon @context requires the exact 4-tuple and all 22 extension keys with @type: "@id"; Mastodon followers?page=1 requires the exact page keys and orderedItems: string[]; regression tests reject extra actor fields, missing/extra context terms, missing @type mappings, extra collection/page fields, arbitrary interactionPolicy, invalid actor extensions, and non-string followers items. Please confirm whether any proof gap remains, especially around exact 1:1 structure and no silently ignored new fields.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== activitypub-schema.ts ==="
cat packages/api/src/api/activitypub-schema.ts

echo ""
echo "=== activitypub-schema-parity.test.ts ==="
cat packages/api/tests/activitypub-schema-parity.test.ts

echo ""
echo "=== http.ts (exactActivityPubParseOptions usage) ==="
grep -n "exactActivityPubParseOptions\|onExcessProperty\|exactDecode\|decodeUnknown" packages/api/src/http.ts | head -40

echo ""
echo "=== http-config.test.ts (exact decode + regression sections) ==="
grep -n "exactActivityPubParseOptions\|onExcessProperty\|extra.*field\|excess\|rejects\|orderedItems.*string\|non-string" packages/api/tests/http-config.test.ts | head -60

echo ""
echo "=== Full http-config.test.ts for followers/page sections ==="
grep -n -A 40 "followers page\|followers?page\|OrderedCollectionPage\|exactDecode\|exactActivity" packages/api/tests/http-config.test.ts | head -200

echo ""
echo "=== Check if exactActivityPubParseOptions is exported from schema or http ==="
rg -n "exactActivityPubParseOptions\|onExcessProperty" packages/api/src/ packages/api/tests/

Length of output: 33122


@skulidropek — full verification complete. Here is the exact parity audit:


✅ Exact 1:1 Structural Parity — All Proof Obligations Satisfied

exactActivityPubParseOptions wired everywhere

{ onExcessProperty: "error" } is exported from activitypub-schema.ts and used in:

  • http.ts line 320: Schema.decodeUnknown(schema, exactActivityPubParseOptions) — HTTP handlers reject before sending a 200
  • http-config.test.ts line 142: decodeOrThrow uses it — test assertions decode strictly
  • activitypub-schema-parity.test.ts: decodeActivityPubEither wrapper passes it to every Schema.decodeUnknownEither call

No silently ignored fields at any boundary.


ActivityPubPersonSchema = exact local | exact Mastodon issue actor ✅

Variant Schema Extra fields rejected
Local LocalActivityPubPersonSchema — exact Struct, 13 required fields ✅ via onExcessProperty: "error"
Mastodon issue MastodonIssueActivityPubPersonSchema — exact Struct, 27 required fields ✅ via onExcessProperty: "error"

Mastodon @context exact 4-tuple, all 22 extensions required ✅

MastodonActorContextExtensionsSchema has all 22 keys non-optional (no Schema.optional anywhere):

Key group Enforced as
manuallyApprovesFollowers, toot, schema, PropertyValue, value, discoverable, suspended, memorial, indexable, showFeatured, showMedia, showRepliesInMedia, gts Schema.String (required)
featured, featuredTags, alsoKnownAs, movedTo, attributionDomains, interactionPolicy, canQuote, automaticApproval, manualApproval JsonLdIdMappingSchema = Struct({ "@id": String, "@type": Literal("@id") }) (required)

Missing any one key → rejected. Extra key → rejected via onExcessProperty: "error".


Mastodon followers?page=1 exact keys + orderedItems: string[]

MastodonFollowersOrderedCollectionPageSchema is an exact Struct:

{ "`@context`": Literal(activityStreams), id: String, type: Literal("OrderedCollectionPage"),
  totalItems: Number, partOf: String, next: String, orderedItems: Array(String) }
  • Matches the live Mastodon nixCraft fixture exactly (7 keys)
  • { id: "not-a-link" } in orderedItems → rejected (not a string)
  • prev: "..." extra field → rejected via onExcessProperty: "error"

Regression tests cover all described rejection cases ✅

Case Test Result
Extra actor field extraField: "..." rejects non-exact ActivityPub fixture shapes isLeft
Missing context term (manualApproval removed) same isLeft
Missing @type mapping on featured same isLeft
Extra context term (extraContextTerm) same isLeft
Mastodon collection + orderedItems: [] same isLeft
Mastodon page + prev: "..." same isLeft
orderedItems: ["ok", { id: "not-a-link" }] same isLeft
interactionPolicy: {} / { arbitrary: true } rejects structurally invalid...extensions isLeft
Invalid icon/image/tag/attachment shapes same isLeft
page=2 returns 400 ApiBadRequestError rejects unsupported followers pages

Property-based tests ✅

12 fc.property tests across ActivityPubPersonSchema, ActivityPubOrderedCollectionSchema, ActivityPubOrderedCollectionPageSchema — accept correct shapes, reject wrong/missing literals and fields.


No proof gap remains. PR #335 implements exact 1:1 structural parity with the issue #334 Mastodon fixtures, enforced at both the schema layer and the HTTP boundary.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
packages/api/src/api/activitypub-schema.ts (2)

189-218: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Схема Mastodon Person сейчас слишком узкая для заявленного parity-контракта.

На Line 201 name сделан обязательным, и в схеме отсутствуют заявленные Mastodon-optional поля (alsoKnownAs, movedTo, suspended, attributionDomains, icon, image, devices и т.д.). С exactActivityPubParseOptions это приведёт к ложным отклонениям валидных actor-документов.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/api/src/api/activitypub-schema.ts` around lines 189 - 218,
MastodonIssueActivityPubPersonSchema is too strict: it marks name as required
and omits many Mastodon-optional actor fields, which will cause valid actors to
be rejected under exactActivityPubParseOptions; update
MastodonIssueActivityPubPersonSchema to make name optional (use Schema.Optional)
and add the optional fields declared by Mastodon parity such as alsoKnownAs
(array/string), movedTo (string), suspended (boolean), attributionDomains
(array/string), icon (ActivityPubImage/Object schema), image
(ActivityPubImage/Object schema), devices (array/object), and any other optional
fields already defined elsewhere (e.g., icon/image schemas or
ActivityPubAttachment/Actor sub-schemas), ensuring each is wrapped with
Schema.Optional and uses the appropriate existing schemas (e.g.,
ActivityPubActorAttachmentSchema, ActivityPubActorTagSchema,
ActivityPubPublicKeySchema) so valid actor documents aren't falsely rejected.

155-162: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

interactionPolicy пропускает пустые approval-массивы.

Текущий Schema.filter проверяет только наличие ключа, но не непустоту списков. Это ослабляет инвариант и пропускает структурно пустые policy-блоки.

🔧 Предлагаемый фикс
 const ActivityPubInteractionApprovalSchema = Schema.Struct({
   automaticApproval: Schema.optional(Schema.Array(Schema.String)),
   manualApproval: Schema.optional(Schema.Array(Schema.String))
 }).pipe(
-  Schema.filter((approval) =>
-    approval.automaticApproval !== undefined ||
-    approval.manualApproval !== undefined)
+  Schema.filter((approval) =>
+    (approval.automaticApproval?.length ?? 0) > 0 ||
+    (approval.manualApproval?.length ?? 0) > 0)
 )

Also applies to: 164-171

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/api/src/api/activitypub-schema.ts` around lines 155 - 162, The
current ActivityPubInteractionApprovalSchema uses Schema.filter to only check
for the presence of automaticApproval/manualApproval keys but allows empty
arrays; update the filter predicate so it only passes when at least one of
automaticApproval or manualApproval is a non-empty array (check length > 0),
i.e., ensure approval arrays are defined and have items. Apply the same change
to the other analogous schema in the file that uses the same pattern (the schema
covering lines ~164-171) so both enforce non-empty approval arrays.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@packages/api/src/api/activitypub-schema.ts`:
- Around line 189-218: MastodonIssueActivityPubPersonSchema is too strict: it
marks name as required and omits many Mastodon-optional actor fields, which will
cause valid actors to be rejected under exactActivityPubParseOptions; update
MastodonIssueActivityPubPersonSchema to make name optional (use Schema.Optional)
and add the optional fields declared by Mastodon parity such as alsoKnownAs
(array/string), movedTo (string), suspended (boolean), attributionDomains
(array/string), icon (ActivityPubImage/Object schema), image
(ActivityPubImage/Object schema), devices (array/object), and any other optional
fields already defined elsewhere (e.g., icon/image schemas or
ActivityPubAttachment/Actor sub-schemas), ensuring each is wrapped with
Schema.Optional and uses the appropriate existing schemas (e.g.,
ActivityPubActorAttachmentSchema, ActivityPubActorTagSchema,
ActivityPubPublicKeySchema) so valid actor documents aren't falsely rejected.
- Around line 155-162: The current ActivityPubInteractionApprovalSchema uses
Schema.filter to only check for the presence of automaticApproval/manualApproval
keys but allows empty arrays; update the filter predicate so it only passes when
at least one of automaticApproval or manualApproval is a non-empty array (check
length > 0), i.e., ensure approval arrays are defined and have items. Apply the
same change to the other analogous schema in the file that uses the same pattern
(the schema covering lines ~164-171) so both enforce non-empty approval arrays.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5ff86183-c9b9-495f-9968-7e0fc39eda3c

📥 Commits

Reviewing files that changed from the base of the PR and between 5d88686 and a5bcc9b.

📒 Files selected for processing (7)
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/api/contracts.ts
  • packages/api/src/api/schema.ts
  • packages/api/src/http.ts
  • packages/api/src/services/federation.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/tests/http-config.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: E2E (Clone auto-open SSH)
  • GitHub Check: Test
  • GitHub Check: E2E (Runtime volumes + SSH)
  • GitHub Check: E2E (Clone cache)
  • GitHub Check: E2E (Browser command)
  • GitHub Check: E2E (Login context)
  • GitHub Check: Lint
  • GitHub Check: E2E (OpenCode)
  • GitHub Check: Final build (windows-latest)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Implement Functional Core, Imperative Shell (FCIS) pattern: CORE layer contains only pure functions with immutable data and mathematical operations; SHELL layer isolates all effects (IO, network, database). Strict dependency direction: SHELL → CORE (never reverse).
Never use any, unknown, eslint-disable, ts-ignore, or as type assertions (except in rigorously justified cases with documentation). Always use exhaustive union type analysis through .exhaustive() pattern matching.
All external dependencies must be wrapped through typed interfaces and injected via Effect-TS Layer pattern. Never call external services directly from CORE functions.
Use monadic composition with Effect-TS for all effects: Effect<Success, Error, Requirements>. Compose effects through pipe() and Effect.flatMap(). Implement dependency injection via Layer pattern. Handle errors without try/catch blocks.
All functions must be pure in the CORE layer: no side effects (logging, console output, IO operations, mutations). Separate all side effects into the SHELL layer.
Use exhaustive pattern matching with Effect.Match instead of switch statements. Example: Match.value(item).pipe(Match.when(...), Match.exhaustive).
Document all functions with comprehensive TSDoc including: @pure (true/false), @effect (required services), @invariant (mathematical invariants), @precondition, @postcondition, @complexity (time and space), @throws Never (errors must be typed in Effect).
Use functional comment markers for code clarity: CHANGE (brief description), WHY (mathematical/architectural justification), QUOTE(ТЗ) (requirement citation), REF (RTM or message ID), SOURCE (external source with quote), FORMAT THEOREM (∀x ∈ Domain: P(x) → Q(f(x))), PURITY (CORE|SHELL), EFFECT (Effect type signature), INVARIANT (mathematical invariant), COMPLEXITY (time/space).
Define all external service dependencies as Context.Tag classes with fully typed methods returning Effect types. Example: `class Da...

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/http.ts
  • packages/api/src/services/federation.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Forbidden constructs in CORE code: any, eslint-disable, ts-ignore, async/await, raw Promise chains (then/catch), Promise.all, try/catch for logic control, console.*, switch statements (use Match with .exhaustive() instead)
All functions must use Effect-TS for composing effects: Effect<Success, Error, Requirements>. No direct async/await, Promise chains, or try/catch in product logic.
Functional comments must include: CHANGE, WHY, QUOTE(ТЗ) or n/a, REF, SOURCE or n/a, FORMAT THEOREM, PURITY (CORE|SHELL), EFFECT signature for SHELL functions, INVARIANT, and COMPLEXITY.
All data mutations must use immutable patterns (ReadonlyArray, readonly properties, Object.freeze); mutation in SHELL only when absolutely necessary and documented.

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/http.ts
  • packages/api/src/services/federation.ts
**/*.{sh,bash,py,js,ts,jsx,tsx,go,java,rb,php}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce command injection or unsafe shell/process execution with user-controlled input

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/http.ts
  • packages/api/src/services/federation.ts
**/*.{py,js,ts,jsx,tsx,go,java,rb,php,sh,bash,c,cpp}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce path traversal or writes outside intended project/container state directories

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/http.ts
  • packages/api/src/services/federation.ts
**/*.{js,ts,jsx,tsx,py,java,go,rb,php,sh,bash,yml,yaml,json,env*,toml,cfg,config,dockerfile,dockerignore}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files expose credentials, tokens, private-keys, or PII in source, generated config, logs, or CI output

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/http.ts
  • packages/api/src/services/federation.ts
packages/api/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (README.md)

API layer should work only with LIB and serve as the intermediary between APP and LIB

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/http.ts
  • packages/api/src/services/federation.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Ты строгий ревьюер SPEC DRIVEN DEVELOPMENT.

Перед выводами изучи README.md, другие *.md файлы, linked issues,
PR description, PR comments/discussion и релевантную кодовую базу.

Сверь изменения с исходным ТЗ/спекой и обсуждением. Флагай любой уход
от спеки, недокументированное изменение поведения, отсутствие тестов
для заявленного поведения и security-риск. Если спека не видна,
попроси автора добавить ее в issue или PR description.

Проверь решение с точки зрения формальной верификации: какие инварианты,
предусловия и постусловия можно доказать математически, а где доказуемость
слабая. Оцени решение с точки зрения теории игр: устойчивы ли стимулы,
нет ли выгодного обхода правил, и какое решение было бы сильнее.

Files:

  • packages/api/src/api/schema.ts
  • packages/api/tests/http-config.test.ts
  • packages/api/src/api/contracts.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
  • packages/api/src/api/activitypub-schema.ts
  • packages/api/src/http.ts
  • packages/api/src/services/federation.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Implement property-based testing using fast-check for mathematical properties and invariants. Example: fc.property(fc.array(messageArbitrary), (messages) => isChronologicallySorted(sortMessagesByTimestamp(messages))).
Mock external dependencies in unit tests using Effect's testing utilities. Run tests without Effect runtime for speed. Example: Effect.provide(MockService), Effect.runPromise.

Files:

  • packages/api/tests/http-config.test.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Property-based tests (fast-check) must verify mathematical invariants; unit tests must use Effect test utilities without async/await.

Files:

  • packages/api/tests/http-config.test.ts
  • packages/api/tests/activitypub-schema-parity.test.ts
🧠 Learnings (1)
📚 Learning: 2026-05-18T07:43:38.131Z
Learnt from: skulidropek
Repo: ProverCoderAI/docker-git PR: 313
File: packages/api/src/services/panel-cloudflare-tunnel.ts:123-135
Timestamp: 2026-05-18T07:43:38.131Z
Learning: In this repo’s ProverCoderAI/docker-git architecture, it is intentional for service modules under packages/api/src/services/ to call Node.js APIs directly (e.g., fs, child_process, fetch). Do not treat direct Node API usage in these service modules as a dependency-injection (DI) violation. This codebase provides the NodeContext at the program boundary (entry point) rather than injecting Context.Tag/Layer services into individual service modules; reviewers should only flag DI/context issues if the expected boundary setup is missing.

Applied to files:

  • packages/api/src/services/federation.ts
🔇 Additional comments (6)
packages/api/src/api/contracts.ts (1)

12-16: LGTM!

Also applies to: 629-640

packages/api/src/api/schema.ts (1)

14-23: LGTM!

packages/api/src/http.ts (1)

47-49: LGTM!

Also applies to: 320-330, 343-351, 719-739

packages/api/src/services/federation.ts (1)

33-36: LGTM!

Also applies to: 630-709, 725-767, 1595-1605

packages/api/tests/activitypub-schema-parity.test.ts (1)

12-20: LGTM!

Also applies to: 243-445, 446-558

packages/api/tests/http-config.test.ts (1)

15-17: LGTM!

Also applies to: 141-155, 274-300

@skulidropek
Copy link
Copy Markdown
Member Author

@coderabbitai можешь проверить на сколько код реализует

{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1","https://purl.archive.org/socialweb/webfinger",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","toot":"http://joinmastodon.org/ns#","featured":{"@id":"toot:featured","@type":"@id"},"featuredTags":{"@id":"toot:featuredTags","@type":"@id"},"alsoKnownAs":{"@id":"as:alsoKnownAs","@type":"@id"},"movedTo":{"@id":"as:movedTo","@type":"@id"},"schema":"http://schema.org/#","PropertyValue":"schema:PropertyValue","value":"schema:value","discoverable":"toot:discoverable","suspended":"toot:suspended","memorial":"toot:memorial","indexable":"toot:indexable","attributionDomains":{"@id":"toot:attributionDomains","@type":"@id"},"showFeatured":"toot:showFeatured","showMedia":"toot:showMedia","showRepliesInMedia":"toot:showRepliesInMedia","gts":"https://gotosocial.org/ns#","interactionPolicy":{"@id":"gts:interactionPolicy","@type":"@id"},"canQuote":{"@id":"gts:canQuote","@type":"@id"},"automaticApproval":{"@id":"gts:automaticApproval","@type":"@id"},"manualApproval":{"@id":"gts:manualApproval","@type":"@id"}}],"id":"https://mastodon.social/users/GordonFreeman","webfinger":"GordonFreeman@mastodon.social","type":"Person","following":"https://mastodon.social/users/GordonFreeman/following","followers":"https://mastodon.social/users/GordonFreeman/followers","inbox":"https://mastodon.social/users/GordonFreeman/inbox","outbox":"https://mastodon.social/users/GordonFreeman/outbox","featured":"https://mastodon.social/users/GordonFreeman/collections/featured","featuredTags":"https://mastodon.social/users/GordonFreeman/collections/tags","preferredUsername":"GordonFreeman","name":"GordonFreeman","summary":"","url":"https://mastodon.social/@GordonFreeman","manuallyApprovesFollowers":false,"discoverable":false,"indexable":false,"published":"2022-05-11T00:00:00Z","memorial":false,"showFeatured":true,"showMedia":true,"showRepliesInMedia":true,"interactionPolicy":{"canFeature":{"automaticApproval":["https://mastodon.social/users/GordonFreeman"]}},"featuredCollections":"https://mastodon.social/ap/users/108283196203417442/featured_collections","publicKey":{"id":"https://mastodon.social/users/GordonFreeman#main-key","owner":"https://mastodon.social/users/GordonFreeman","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0zWlvp7jHBsNV/3QkBnu\nZpbtPWetfBFJsK7huV3ZLsUk+oeyAPQKO4KCiZiBZm3qVBCr6O7p75GAesXRpMVN\ngkrgzFCI4ZME0x/6VrVNvo4q8xpRxu+rAsULXa1mI9XiUDDcFTIJn8wW09uGa4XK\nW0KGwe2+F6q7OVJfkiAXbwRrjBg6Stn8NOd6n/TYFw4UnyMNZ6BPqnxxhEv1GsFn\n6viF/jrT6SpIY0K6kBlMk5AepFj/5LpoiTdH7bfpja3jbGQNF/h1PJ0U1kP4JvNT\nNOnWMS+5fMovjftxPwwqggB8FrHeDF1QOk8ETznugukqhtNpLTx28P+sEAJC8eZS\nPwIDAQAB\n-----END PUBLIC KEY-----\n"},"tag":[],"attachment":[],"endpoints":{"sharedInbox":"https://mastodon.social/inbox"}}

Skuli Dropek, [19.05.2026 22:04]
Вот как пример от mastodon

Skuli Dropek, [19.05.2026 22:06]
Именно follows запроси curl -s -H "Accept: application/activity+json"
"https://mastodon.social/users/nixCraft/followers?page=1"

Skuli Dropek, [19.05.2026 21:43]
https://forgefed.org/spec/
Добавь tpyechecker @context
Должно быть 1 в 1

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== activitypub-schema.ts ==="
cat packages/api/src/api/activitypub-schema.ts

Length of output: 8763


@skulidropek — вот свежий построчный аудит по текущему коду:


@context — точный 4-элементный Tuple, все 22 ключа

MastodonActorJsonLdContextSchema = Tuple(activityStreams, security, socialWebWebfinger, MastodonActorContextExtensionsSchema)

Ключ фикстуры Значение Схема Статус
manuallyApprovesFollowers "as:..." Schema.String
toot "http://joinmastodon.org/ns#" Schema.String
featured {"@id":"toot:featured","@type":"@id"} JsonLdIdMappingSchema
featuredTags {"@id":..., "@type":"@id"} JsonLdIdMappingSchema
alsoKnownAs {"@id":..., "@type":"@id"} JsonLdIdMappingSchema
movedTo {"@id":..., "@type":"@id"} JsonLdIdMappingSchema
schema "http://schema.org/#" Schema.String
PropertyValue "schema:PropertyValue" Schema.String
value "schema:value" Schema.String
discoverable/suspended/memorial/indexable strings Schema.String
attributionDomains {"@id":..., "@type":"@id"} JsonLdIdMappingSchema
showFeatured/showMedia/showRepliesInMedia strings Schema.String
gts "https://gotosocial.org/ns#" Schema.String
interactionPolicy/canQuote/automaticApproval/manualApproval {"@id":..., "@type":"@id"} JsonLdIdMappingSchema

Все 22 ключа — required (без Schema.optional). Лишний ключ → отклонение через onExcessProperty: "error".


MastodonIssueActivityPubPersonSchema — все 27 полей фикстуры

Поле фикстуры Тип Схема Статус
id, webfinger, preferredUsername, name, summary, url, published, featuredCollections string Schema.String
following, followers, inbox, outbox, featured, featuredTags string Schema.String
manuallyApprovesFollowers, discoverable, indexable, memorial, showFeatured, showMedia, showRepliesInMedia boolean Schema.Boolean
interactionPolicy {canFeature:{automaticApproval:[...]}} MastodonInteractionPolicySchema
publicKey {id, owner, publicKeyPem} ActivityPubPublicKeySchema
tag [] Array(Union(Hashtag,Emoji))
attachment [] Array(PropertyValue)
endpoints {sharedInbox:...} ActivityPubEndpointsSchema

interactionPolicy — фиктура декодируется корректно

Фикстура: {"canFeature":{"automaticApproval":["https://mastodon.social/users/GordonFreeman"]}}

  • MastodonInteractionPolicySchema — filter: canFeature присутствует → проходит ✅
  • ActivityPubInteractionApprovalSchema — filter: automaticApproval присутствует → проходит ✅

followers?page=1 (nixCraft live shape)

MastodonFollowersOrderedCollectionPageSchema:

Поле Live Mastodon Схема Статус
@context "https://www.w3.org/ns/activitystreams" Literal(activityStreams)
id URL страницы Schema.String
type "OrderedCollectionPage" Literal(...)
totalItems число Schema.Number
partOf URL коллекции Schema.String
next URL следующей страницы Schema.String
orderedItems ["https://..."] Array(Schema.String)

Итог

Код реализует оба JSON-шейпа (Mastodon actor + followers?page=1) с точностью 1 в 1:

  • Все 22 ключа @context с правильными типами (String vs JsonLdIdMappingSchema)
  • Все 27 полей Person-актора
  • Точная структура interactionPolicy
  • onExcessProperty: "error" на HTTP-границе и в тестах — ни одно лишнее поле не проходит молча
  • Пустые массивы tag:[] и attachment:[] валидны как Array(...) схемы

@skulidropek skulidropek merged commit 464fc34 into main May 21, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Можешь ещё раз сверить совпадение этим условиям? У нас типы должны быть 1 в 1

1 participant