FEP-0837: Federated Marketplace
by silverpill silverpill@firemail.cc submited on 2023-08-17
Summary
This document describes a minimal implementation of a federated marketplace based on ActivityPub protocol and Valueflows vocabulary. In such marketplace actors can publish offers and requests, respond to offers and requests published by other actors, enter into agreements and exchange information necessary to complete these agreements.
History
Extension of ActivityPub protocol with Valueflows vocabulary was initially proposed by Lynn Foster in FEP-d767.
Requirements
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC-2119.
Proposals
Valueflows defines proposals as published requests or offers, sometimes with what is expected in return.
The representation of a proposal is a JSON document with the following properties:
id
(REQUIRED): the proposal’s unique global identifier.type
(REQUIRED): the type of the object SHOULD beProposal
. If interoperability with other ActivityPub services is desirable, implementers MAY use object types from Activity Vocabulary, such asNote
.attributedTo
(REQUIRED): the actor who published the proposal.name
(RECOMMENDED): the title of the proposal.content
(OPTIONAL): the description of the proposal. The type of content SHOULD betext/html
.published
(RECOMMENDED): the date and time at which the proposal was published.location
(OPTIONAL): indicates a physical location associated with the proposal. The representation of location MUST conform to the recommendations of Activity Vocabulary document, section 5.3 Representing Places.publishes
(REQUIRED): the primary intent of this proposal (see below).reciprocal
(OPTIONAL): the reciprocal intent of this proposal (see below).unitBased
(REQUIRED): indicates whether the quantities expressed in the proposal can be multiplied or not.to
(REQUIRED): the audience of the proposal.
Intents are proposed economic transactions. The primary intent describes what is being offered or requested, and reciprocal intent describes what is expected or offered in return. Some examples:
- A good is offered in exchange for money. Transfer of a good is a primary intent and a money transfer is a reciprocal intent.
- A good is offered as a gift. Transfer of a good is a primary intent and there’s no reciprocal intent.
- Service is requested in exchange for money. Delivery of a service is a primary intent and money transfer is a reciprocal intent.
Whether proposal is an offer or a request is determined by the properties of the primary intent (see below).
The representation of an intent is a JSON document with the following properties:
id
(REQUIRED): the unique global identifier of the intent. Implementations SHOULD use URL fragments to identify intents associated with a given proposal. The RECOMMENDED fragment identifiers for primary and reciprocal intents areprimary
andreciprocal
.type
(REQUIRED): the type of the object MUST beIntent
.action
(REQUIRED): the type of economic transaction. The value of this property SHOULD be eitherdeliverService
ortransfer
.resourceConformsTo
(RECOMMENDED): the type of an economic resource. Could be any URI.resourceQuantity
(REQUIRED): the amount and unit of the economic resource. This is an object with two properties:hasUnit
(REQUIRED): name of the unit, according to Ontology of units of Measure classification. The RECOMMENDED unit for countable items isone
.hasNumericalValue
(REQUIRED): amount of the resource.availableQuantity
(OPTIONAL): the quantity of the offered resource currently available.provider
: the actor who provides the resource. This property is REQUIRED for offered resources. If used in the primary intent, the proposal is considered an offer and the value of this property MUST match the value ofattributedTo
property.receiver
: the actor who receives the resource. This property is REQUIRED for requested resources. If used in the primary intent, the proposal is considered a request and the value of this property MUST match the value ofattributedTo
property.
Example:
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"om2": "http://www.ontology-of-units-of-measure.org/resource/om-2/",
"vf": "https://w3id.org/valueflows/",
"Proposal": "vf:Proposal",
"Intent": "vf:Intent",
"receiver": "vf:receiver",
"provider": "vf:provider",
"action": "vf:action",
"unitBased": "vf:unitBased",
"publishes": "vf:publishes",
"reciprocal": "vf:reciprocal",
"resourceConformsTo": "vf:resourceConformsTo",
"resourceQuantity": "vf:resourceQuantity",
"hasUnit": "om2:hasUnit",
"hasNumericalValue": "om2:hasNumericalValue"
}
],
"type": "Proposal",
"id": "https://market.example/proposals/ddde9d6f-6f3b-4770-a966-3a18ef006930",
"attributedTo": "https://market.example/users/alice",
"name": "Offering used bike",
"content": "Blue one-speed bike, 15 years old, some rust",
"published": "2023-06-18T19:22:03.918737Z",
"location": {
"type": "Place",
"longitude": -71.0,
"latitude": 25.0
},
"publishes": {
"type": "Intent",
"id": "https://market.example/proposals/ddde9d6f-6f3b-4770-a966-3a18ef006930#primary",
"action": "transfer",
"resourceConformsTo": "https://www.wikidata.org/wiki/Q11442",
"resourceQuantity": {
"hasUnit": "one",
"hasNumericalValue": "1"
},
"availableQuantity": {
"hasUnit": "one",
"hasNumericalValue": "1"
},
"provider": "https://market.example/users/alice"
},
"reciprocal": {
"type": "Intent",
"id": "https://market.example/proposals/ddde9d6f-6f3b-4770-a966-3a18ef006930#reciprocal",
"action": "transfer",
"resourceConformsTo": "https://www.wikidata.org/wiki/Q4917",
"resourceQuantity": {
"hasUnit": "one",
"hasNumericalValue": "30"
},
"receiver": "https://market.example/users/alice"
},
"unitBased": false,
"to": "https://www.w3.org/ns/activitystreams#Public"
}
Publishing a proposal
Proposals can be linked to actors (if actor provides a service) or to other objects (if they represent economic resources) using FEP-0ea0 payment links. Proposals can also be added to public collections, or be delivered to actor’s followers using Create
activity, or announced by group actors.
If FEP-0ea0 payment link is used, its href
attribute MUST contain the proposal ID and its rel
array MUST contain the string https://w3id.org/valueflows/Proposal
. The value of mediaType
attribute SHOULD be application/ld+json; profile="https://www.w3.org/ns/activitystreams"
.
Example of a proposal attached to an actor via payment link:
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Person",
"id": "https://market.example/users/alice",
"inbox": "https://market.example/users/alice",
"outbox": "https://market.example/users/alice",
"attachment": [
{
"type": "Link",
"name": "Buy a bike",
"mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
"href": "https://market.example/proposals/ddde9d6f-6f3b-4770-a966-3a18ef006930",
"rel": ["payment", "https://w3id.org/valueflows/Proposal"]
}
]
}
Consuming implementations which don’t have marketplace features MAY display proposals similarly to Note
objects.
Responding to a proposal
Agreements
An interested party responds to a proposal and then parties start negotiating to reach an agreement.
To respond to a proposal, an interested party MUST send an Agreement
object wrapped in Offer
activity to the actor indicated by the attributedTo
property of the proposal. The proposing party MUST either commit to the action described in the proposal or send a rejection.
In the first case, the proposer finalizes the agreement and sends Accept(Offer)
activity back to the interested party.
In the second case, the proposer sends Reject(Offer)
activity. The interested party MAY send Offer(Agreement)
activities many times until agreement is reached.
The representation of an agreement is a JSON document with the following properties:
id
(OPTIONAL): the unique global identifier of the agreement. This property is REQUIRED for finalized agreements.type
(REQUIRED): the type of the object MUST beAgreement
.clauses
(REQUIRED): the list of commitments associated with the agreement.
Commitments are promised economic transactions. The representation of a commitment is a JSON document with the following properties:
id
(OPTIONAL): the unique global identifier of the commitment. This property is REQUIRED for commitments in finalized agreements. Implementations SHOULD use URL fragments to identify commitments associated with a given agreement. The RECOMMENDED fragment identifiers for commitments satisfying primary and reciprocal intents of the proposal areprimary
andreciprocal
.type
(REQUIRED): the type of the object MUST beCommitment
.satisfies
(REQUIRED): the reference to an intent.resourceQuantity
(REQUIRED): the amount and unit of the economic resource.
The first commitment MUST satisfy the primary intent of the proposal. The second commitment MUST satisfy the reciprocal intent of the proposal.
If the value of unitBased
property of the proposal is false
, the amount of resources specified in commitments MUST be equal to amounts specified in the proposal. Otherwise, amounts MUST be multiples of amounts specified in the proposal.
Example of an Offer(Agreement)
activity:
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"om2": "http://www.ontology-of-units-of-measure.org/resource/om-2/",
"vf": "https://w3id.org/valueflows/",
"Agreement": "vf:Agreement",
"clauses": "vf:clauses",
"Commitment": "vf:Commitment",
"satisfies": "vf:satisfies",
"resourceQuantity": "vf:resourceQuantity",
"hasUnit": "om2:hasUnit",
"hasNumericalValue": "om2:hasNumericalValue"
}
],
"type": "Offer",
"id": "https://social.example/objects/fc4af0d2-c3a1-409b-947c-3c5be29f49b0/offer",
"actor": "https://social.example/users/bob",
"object": {
"type": "Agreement",
"clauses": [
{
"type": "Commitment",
"satisfies": "https://market.example/proposals/ddde9d6f-6f3b-4770-a966-3a18ef006930#primary",
"resourceQuantity": {
"hasUnit": "one",
"hasNumericalValue": "1"
}
},
{
"type": "Commitment",
"satisfies": "https://market.example/proposals/ddde9d6f-6f3b-4770-a966-3a18ef006930#reciprocal",
"resourceQuantity": {
"hasUnit": "one",
"hasNumericalValue": "30"
}
}
]
},
"to": "https://market.example/users/alice"
}
Accepting an agreement
The object
of Accept
activity MUST be the id
of the Offer
activity previously sent to the actor.
Accept
activity MUST have the result
property containing the Agreement
object. The finalized agreement and corresponding commitments MUST have an id
property. If a similar agreement between parties already exists, it MAY be updated and its id
re-used. The quantities specified in the finalized agreement MUST match the quantities specified in Agreement
object from the Offer
activity.
The finalized agreement MAY have url
property containing one or more links to resources associated with the agreement. An example of such resource is a payment page (which can be represented as FEP-0ea0 link).
Example:
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"om2": "http://www.ontology-of-units-of-measure.org/resource/om-2/",
"vf": "https://w3id.org/valueflows/",
"Agreement": "vf:Agreement",
"clauses": "vf:clauses",
"Commitment": "vf:Commitment",
"satisfies": "vf:satisfies",
"resourceQuantity": "vf:resourceQuantity",
"hasUnit": "om2:hasUnit",
"hasNumericalValue": "om2:hasNumericalValue"
}
],
"type": "Accept",
"id": "https://market.example/activities/059f08fa-31b1-4136-8d76-5987d705a0ab",
"actor": "https://market.example/users/alice",
"object": "https://social.example/objects/fc4af0d2-c3a1-409b-947c-3c5be29f49b0/offer",
"result": {
"type": "Agreement",
"id": "https://market.example/agreements/edc374aa-e580-4a58-9404-f3e8bf8556b2",
"clauses": [
{
"id": "https://market.example/agreements/edc374aa-e580-4a58-9404-f3e8bf8556b2#primary",
"type": "Commitment",
"satisfies": "https://market.example/proposals/ddde9d6f-6f3b-4770-a966-3a18ef006930#primary",
"resourceQuantity": {
"hasUnit": "one",
"hasNumericalValue": "1"
}
},
{
"id": "https://market.example/agreements/edc374aa-e580-4a58-9404-f3e8bf8556b2#reciprocal",
"type": "Commitment",
"satisfies": "https://market.example/proposals/ddde9d6f-6f3b-4770-a966-3a18ef006930#reciprocal",
"resourceQuantity": {
"hasUnit": "one",
"hasNumericalValue": "30"
}
}
],
"url": {
"type": "Link",
"href": "https://pay.example/invoices/7f1f0c81-0108-4c91-9cb1-d38ebccc3aa1",
"rel": "payment"
}
},
"to": "https://social.example/users/bob"
}
Rejecting an agreement
The object
of Reject
activity MUST be the id
of the Offer
activity previously sent to the actor.
Activity MAY contain content
property indicating the reason for rejection.
Example:
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Reject",
"id": "https://market.example/activities/8c05f97f-1531-4b70-9ca8-4ee4a09f36a4",
"actor": "https://market.example/users/alice",
"object": "https://social.example/objects/fc4af0d2-c3a1-409b-947c-3c5be29f49b0/offer",
"content": "Not available",
"to": "https://social.example/users/bob"
}
Confirmations
Economic transaction happens outside the protocol. When both parties complete their parts of the transaction, the proposing party MUST publish a confirmation.
The type and structure of confirmation activity may vary between different marketplaces, but it MUST contain a reference to the Agreement
object. The context
property is RECOMMENDED for this purpose.
Example:
{
"@context": [
"https://www.w3.org/ns/activitystreams"
],
"type": "Create",
"id": "https://market.example/receipts/ad2f7ee1-6567-413e-a10b-72650cbdc743/create",
"actor": "https://market.example/users/alice",
"object": {
"type": "Document",
"id": "https://market.example/receipts/ad2f7ee1-6567-413e-a10b-72650cbdc743",
"context": "https://market.example/agreements/edc374aa-e580-4a58-9404-f3e8bf8556b2",
"published": "2023-07-03T14:13:41.843794Z"
},
"to": "https://social.example/users/bob"
}
References
- [ActivityPub] Christine Lemmer Webber, Jessica Tallon, ActivityPub, 2018
- [Valueflows] Lynn Foster, elf Pavlik, Bob Haugen, Valueflows, 2023
- [FEP-d767] Lynn Foster, FEP-d767: Extend ActivityPub with Valueflows, 2023
- [RFC-2119] S. Bradner, Key words for use in RFCs to Indicate Requirement Levels, 1997
- [Activity Vocabulary] James M Snell, Evan Prodromou, Activity Vocabulary, 2017
- [Ontology of units of Measure] Hajo Rijgersberg, Don Willems, Xin-Ying Ren, Mari Wigham, Jan Top, Ontology of units of Measure, 2017
- [FEP-0ea0] silverpill, FEP-0ea0: Payment Links, 2023
Copyright
CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
To the extent possible under law, the authors of this Fediverse Enhancement Proposal have waived all copyright and related or neighboring rights to this work.