FEP-d767: Extend ActivityPub with Valueflows

Hello!

This is a discussion thread for the proposed FEP-d767: Extend ActivityPub with Valueflows. Please use this thread to discuss the proposed FEP and any potential problems or improvements that can be addressed.

Summary

A standard method to extend ActivityPub/ActivityStream with Valueflows vocabulary, to enable varied economic networking activity in the fediverse.

2 Likes

@lynnfoster, the following is just a technical comment.

First, in order for activities to be distributed in ActivityPub, they should have recipients, i.e. to, cc, bto, bcc, audience. It would be good if your examples could include them.

In a similar note, it would be advantageous for all objects that can be considered “owned by someone” to have this owner as an attributedTo property. So taking your first example, it would result in something akin to:

{
  "@context": [...],
  "type": "Create",
  "actor": {
    "type": "Person",
    "name": "Sally"
  },
  "to": {
      "type": "Group",
      "name": "SocialHub"
  },
  "object": {
    "attributedTo": {
       "type": "Person",
      "name": "Sally"
     },
    "type": "vf:Plan",
    "content": "Collaborate on defining the VF extension to AP/AS. Propose as a FEP (Fediverse Enhancement Proposal).",
    "name": "Define AP-VF Extension"
  },
  "summary": "Sally created a project plan."
}

Second, in order for the first object to be valid json-ld, it should have the form:

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "vf": "https://w3id.org/valueflows/"
    }
  ],
  "type": "Create",
  "actor": {
    "type": "Person",
    "name": "Sally"
  },
  "object": {
    "type": "vf:Plan",
    "content": "Collaborate on defining the VF extension to AP/AS. Propose as a FEP (Fediverse Enhancement Proposal).",
    "name": "Define AP-VF Extension"
  },
  "summary": "Sally created a project plan."
}

I changed the @context to be a list of a string, and an object.

Third, relating to

"vf:name": "HUMANs", # could use target for this?

HUMANs is probably the meaning assigned to as:Public, i.e. the usually used public collection. I’m of course wondering now, if you mean by humans as in “public without bots”?

I hope this helps.

1 Like

One question we have about how to best extend AP with something like Valueflows (VF). More to come later. This has to do with what is best as an Activity vs. an Object.

In the VF model, there are things that are like activities, in relation to objects, in that they are “edges” on the directed graph created in VF, the “resource flows” between agents. One way to look at them is that they are “proposals” (offers, requests), “commitments” (promises to do something like some work, or transfer something for exchange), “economic events” (record you did something or transferred something. Another way to look at them, which sound more like activities, is they all have an “action” to let VF know what to do with the resources. Examples are “produce”, “deliver service”, “consume”, “use”, “work”, “pick up”, “drop off”, “transfer”, “move”. But those are used at all three of the stages above. There are also other things that can be thought of as verbs, like “plan”, which includes a Plan, some Processes, some Commitments. Or “commit”, when someone promises to do or provide something.

When @mayel from Bonfire and I discussed this, actually some years ago, we came to the conclusion that we should just make VF concepts all into AS Objects, and use activities Create, Update, Delete. And maybe a few more like Accept, Reject, tbd. This keeps AP/AS and VF the least entangled, as things move forward with new versions, in the various documentation, etc. Or as different extensions want the same activity. And it keeps things conceptually simpler. But we did have some advice from an experienced fediverse dev that we should try to create appropriate new activities. As of now the 2 or 3 projects that have implemented VF as an extension do it the first way, everything is an object, using Bonfire as the example. And I like it, in that it uses AP/AS for the messaging capability, and keeps the content separate. But now the activities are kind of a mixed thing.

It seems like there might be already, or should be in the future, some cross-fediverse consensus about how to extend AP/AS in general, as relates to this question. So, all advice is welcome. Or if I haven’t looked at appropriate examples where there are similar extensions, links would be really helpful.

1 Like

Another question we have is about complex Objects. Is there any problem with that? The software that receives the data would of course need to know how to handle them, as well as when they come one at a time, which they also might.

One example is a Plan might have many connected Processes, each of which has input and output Commitments or Intents (nobody has committed yet). Each Process and each Commitment will reference various existing data also.

TL;DR: I have a strong preference for using Create, Update, and Delete as much as possible.

Just using these makes interoperability with my project bovine “easy”. By this, I mean that objects should behave under these operations as one expects. In order to demonstrate this (aka brag), I put together a test case for it. It demonstrates that

  • Create a Valueflows Object
  • Delete a Valueflows Object

works as one would expect. Side note: I needed to add ids to the objects for everything to work nicely.


As far as other interactions with objects are concerned, e.g. Like, I believe that one can probably abstract these with an interactions collection that includes all Activities whose object has a certain id. This would allow one to add custom Interactions, e.g. EmojiReaction.

If one builds this correctly, it would also include Activities from something like Valueflows.

1 Like

A standard method to extend ActivityPub/ActivityStream with Valueflows vocabulary, to enable varied economic networking activity in the fediverse.

@lynnfoster In the context of this FEP, what is an economic activity? Can this vocabulary be used to describe a simple exchange, like sending an invoice (seller → buyer) and notifying about a payment (buyer → seller)?

Yes, except the model is more abstracted and simplified than that. It’s based on an ontology developed in academia called REA (Resources-Events-Agents). So a simple exchange is 2 reciprocal economic events, where a resource goes from agent A to agent B, and another resource goes from agent B to agent A. So the same structure will support a traditional sale, or barter, or mutual credit, all of which are just simple exchanges. Before the actual events, there might be an agreement with 2 corresponding commitments (promises), which you’d need to create an invoice after say one of the commitments is fulfilled. But the invoice is thought of more like an artifact or a report, i.e there’s no “invoice” object.

The other big piece of “economic activity” is coordinating production and distribution.

1 Like

In the specific case I’m thinking about, a purchase of a good (or a service) can be represented with 3 activities. If we use the terms from ActivityStreams vocabulary, the process may look like this:

  • Buyer sends Offer to a seller, where the object property denotes an action (e.g. Buy) and target property points a product.
  • Seller sends TentativeAccept to a buyer, where object property points to Offer activity and target property points to an invoice.
  • Once payment is completed (via a side channel), seller sends Accept to a buyer, with the same properties as the previous activity.

It is possible to represent the same sequence using the Valueflows terms without increasing the number of activities and overall complexity?

The idea of a federated marketplace comes up from time to time in discussions, so I think it would be good to present a solution to this problem in “Examples” section of this FEP.

First point on this:

https://w3id.org/valueflows#AccountableEffect

Does not give back linked data, and it should

rapper -g https://w3id.org/valueflows#AccountableEffect
rapper: Parsing URI https://w3id.org/valueflows#AccountableEffect with parser guess
rapper: Serializing with serializer ntriples
rapper: Error -  - XML parser error: Entity 'middot' not defined

I will grant you that AS2/AP itself was released in a state that was not a very high standard of Linked Data, itself. But, that can perhaps be cleaned up. And new FEPs should really have a certain bar of quality. Namely if you dereference the vocab term, for any of the mime types that are available (preferably only one!) it should give back, valid Linked Data.

It is possible to represent the same sequence using the Valueflows terms without increasing the number of activities and overall complexity?

@silverpill sorry for the delay. Here’s one possibility, although I’m having trouble understanding exactly how the marketplace would work in ActivityPub thinking. And how a pattern of more tightened up messaging between actors should work for economic activity, vs social activity. Might open a more general issue for that after my partner @bhaugen thinks more about it, he has the distributed transaction background and I don’t.

I have more messages, but I think that is not added complexity, just more for completeness of the use case.

First the Seller posts the product offered in the marketplace. (Happened before your sequence.) This potentially could be an as:Offer, not an as:Create.

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "vf": "https://w3id.org/valueflows/",
      "om2": "http://www.ontology-of-units-of-measure.org/resource/om-2/"
    }
  ],
  "summary": "Alice posts an offer of a bike in the Fedi Market.",
  "type": "Create",
  "actor": {
    "id": "https://social.example/alice",
    "type": "Person",
    "name": "Alice"
  },
  "to": {
    "id": "https://fedi.example/market",
    "type": "Group",
    "name": "Fedi Market"
  },
  "object": {
    "type": "vf:Proposal",
    "id": "https://alice.example/proposal/1",
    "vf:name": "Offering used bike",
    "vf:publishes": {
      "type": "vf:Intent",
      "id": "https://alice.example/intent/11",
      "vf:action": "transfer"
      "vf:resourceInventoriedAs": "https://alice.example/resource/bike1"
      "content": "Blue one-speed bike, 15 years old, some rust.",
      "vf:provider": "https://social.example/alice",
    },
    "vf:reciprocal": {
      "type": "vf:Intent",
      "id": "https://alice.example/intent/12",
      "vf:action": "transfer"
      "vf:resourceConformsTo": "https://www.wikidata.org/wiki/Q4917", # US Dollar
      "vf:resourceQuantity": {
        "type": "om2:Measure",
        "om2:hasNumericalValue": "30",
        "om2:hasUnit": "each"
      },
      "vf:receiver": "https://social.example/alice",
    },
  }
}

The Buyer sees the offer, and responds. This corresponds to your first bullet, except that I am thinking of this more like an order, which maybe is like an offer to buy, or seems more definite? I’m imagining some level of marketplace fediverse instance. But there are certainly other ways to imagine it.

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "vf": "https://w3id.org/valueflows/",
      "om2": "http://www.ontology-of-units-of-measure.org/resource/om-2/"
    }
  ],
  "summary": "Bob orders bike from Alice.",
  "type": "Create",
  "actor": {
    "id": "https://pub.example/bob",
    "type": "Person",
    "name": "Bob"
  },
  "to": {
    "id": "https://social.example/alice",
    "type": "Person",
    "name": "Alice"
  },
  "cc": {
    "id": "https://fedi.example/market",
    "type": "Group",
    "name": "Fedi Market"
  },
  "object": {
    "type": "vf:Agreement",
    "id": "https://fedi.example/agreement/1",
    "vf:clauses": [ {
        "type": "vf:Commitment",
        "id": "https://fedi.example/commitment/5",
        "vf:satisfies": "https://alice.example/intent/11",
        "vf:action": "transfer",
        "vf:resourceInventoriedAs": "https://alice.example/resource/bike9",
        "vf:provider": "https://social.example/alice",
        "vf:receiver": "https://pub.example/bob"
      },
      {
        "type": "vf:Commitment",
        "id": "https://fedi.example/commitment/6",
        "vf:satisfies": "https://alice.example/intent/12",
        "vf:action": "transfer",
        "vf:resourceConformsTo": "https://www.wikidata.org/wiki/Q4917", # US Dollar
        "vf:resourceQuantity": {
            "type": "om2:Measure",
            "om2:hasNumericalValue": "30",
            "om2:hasUnit": "each"
        },
        "vf:provider": "https://pub.example/bob",
        "vf:receiver": "https://social.example/alice"
      },
    ]
  }
}

Then there is something like your bullet point 2, because the order needs confirmation from the Seller. Unless this isn’t really needed unless there is a problem and the order can’t be fulfilled? If so, that would save a step.

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "vf": "https://w3id.org/valueflows/",
      "om2": "http://www.ontology-of-units-of-measure.org/resource/om-2/"
    }
  ],
  "summary": "Alice confirms order for bike to Bob.",
  "type": "Accept",
  "actor": {
    "id": "https://social.example/alice",
    "type": "Person",
    "name": "Alice"
  },
  "to": {
    "id": "https://pub.example/bob",
    "type": "Person",
    "name": "Bob"
  },
  "cc": {
    "id": "https://fedi.example/market",
    "type": "Group",
    "name": "Fedi Market"
  },
  "object": {
    "type": "vf:Agreement",
    "id": "https://fedi.example/agreement/1"
  }
}

Then the exchange actually happens. I’ve recorded both sides of this, the order would depend on how the marketplace operates. Seems like most e-commerce wants payment first, but also seems like that could depend on the level of trust within the group. The payment is your bullet 3, the bike transfer is just for completeness. I don’t know if we would need some kind of accept or countersign kind of thing so that both parties on each transfer explicitly say it happened? For now, I’m assuming not, and that since the receiver is recording the economic events, that is good enough. Or, the provider could be recording the events, and if something doesn’t arrive, that is an exception that gets handled.

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "vf": "https://w3id.org/valueflows/",
      "om2": "http://www.ontology-of-units-of-measure.org/resource/om-2/"
    }
  ],
  "summary": "Alice receives US Dollars from Bob.",
  "type": "Create",
  "actor": {
    "id": "https://social.example/alice",
    "type": "Person",
    "name": "Alice"
  },
  "to": {
    "id": "https://pub.example/bob",
    "type": "Person",
    "name": "Bob"
  },
  "cc": {
    "id": "https://fedi.example/market",
    "type": "Group",
    "name": "Fedi Market"
  },
  "object": {
    "type": "vf:EconomicEvent",
    "id": "https://fedi.example/event/1",
    "vf:fulfills": "https://fedi.example/commitment/6",
    "vf:action": "transfer",
    "vf:resourceConformsTo": "https://www.wikidata.org/wiki/Q4917", # US Dollar
    "vf:resourceQuantity": {
      "type": "om2:Measure",
      "om2:hasNumericalValue": "30",
      "om2:hasUnit": "each"
    },
    "vf:provider": "https://pub.example/bob",
    "vf:receiver": "https://social.example/alice",
  }
}
{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "vf": "https://w3id.org/valueflows/",
      "om2": "http://www.ontology-of-units-of-measure.org/resource/om-2/"
    }
  ],
  "summary": "Bob receives bike from Alice.",
  "type": "Create",
  "actor": {
    "id": "https://pub.example/bob",
    "type": "Person",
    "name": "Bob"
  },
  "to": {
    "id": "https://fedi.example/market",
    "type": "Group",
    "name": "Fedi Market"
  },
  "cc": {
    "id": "https://social.example/alice",
    "type": "Person",
    "name": "Alice"
  },
  "object": {
    "type": "vf:EconomicEvent",
    "id": "https://fedi.example/event/2",
    "vf:fulfills": "https://fedi.example/commitment/5",
    "vf:action": "transfer",
    "vf:resourceInventoriedAs": "https://alice.example/resource/bike9",
    "vf:resourceQuantity": {
      "type": "om2:Measure",
      "om2:hasNumericalValue": "1",
      "om2:hasUnit": "each"
    },
    "vf:provider": "https://social.example/alice",
    "vf:receiver": "https://pub.example/bob"
  }
}
1 Like

@helge Thanks all for the feedback!

First, in order for activities to be distributed in ActivityPub, they should have recipients, i.e. to, cc, bto, bcc, audience.

Thanks! Trying to do that now here, will update the actual FEP examples eventually. And also correcting the @context.

In a similar note, it would be advantageous for all objects that can be considered “owned by someone” to have this owner as an attributedTo property.

Not sure I understand this yet, still thinking about ownership of these kinds of objects. Many of them involve collaborations. But I’ll take a deeper look.

“vf:name”: “HUMANs”, # could use target for this?

HUMANs is probably the meaning assigned to as:Public, i.e. the usually used public collection.

Sorry that was confusing. No, HUMANs is a mutual aid network we work with. I’ll change it in the example to something else.

And, thanks for your test, very cool! As a thank-you, I took a picture for you of one of the bovines that live next door to us. :wink:
bovines

Does not give back linked data, and it should

@melvincarvalho thanks for trying this! We do know that and have an open issue, which clearly needs to get prioritized.

1 Like

The placement of a product (bike) and its delivery are omitted in my example, so it looks like our activity sequences are equivalent and have the same number of messages. I think that answers my questions, thanks!

I have a related question: Do you plan on using the followers mechanic with Valueflows? Or will it be based on specified recipients?

Good question. Tentative thoughts:

I think we would want to use the followers collection as it is used in AP. So random people could follow something in VF, meaning they are observing and get copies of posts, but aren’t participating as a member of a group or process (doing work together) or exchange or whatever.

And then we’ll want something specific (and new I think) for people participating in something. Maybe new types of collections? Or maybe just some logic in apps (frontend? backend?) that knows rules, like everyone participating in a process gets a copy of posts of activity to that process, etc.

A topic for some study, and some discussion with Bonfire when they have headspace, they may have some of this in code. @mayel @ivan

1 Like

I’ve been reviewing this sequence and got a couple of questions.

  • In this proposal we have a bike, so the action is transfer. What if provider is doing something in exchange for payment? Should we use deliverService or work (Actions - Valueflows)?

  • In reciprocal intent, you used resourceConformsTo property, but in examples on ValueFlows website resourceClassifiedAs is used to specify currencies. Could you explain the difference between these properties? Also, is any URI allowed?

  • The unit of measure is each but I can’t find it in OM2 vocabulary. In other examples, one is used. Which unit is preferable for currencies?

I’m wondering, how negotiation could be represented? In some cases Agreement can be finalized only by the Seller. Perhaps Buyer should first send Create(Commitment) activity, or even Commit(), which can be accepted or rejected by Seller?

@silverpill you have been digging into this!

  • In this proposal we have a bike, so the action is transfer. What if provider is doing something in exchange for payment? Should we use deliverService or work (Actions - Valueflows)?

Yes, either could make sense, depending on the situation. work is usually part of a plan, and is input to a process; then might be put out as a request if the people doing the planning don’t have anyone to do that work; or you could offer work, usually with a time unit like hours. deliverService is an output of a process, which might include more than one input; it could be a time unit, but more often is something delivered, like a taxi ride or a haircut. I think in your marketplace use case, which doesn’t care about processes, deliverService would be more usual as an offer. It could still be by the hour, but more usually not.

The more general answer is: yes you can use Actions other than transfer as part of an exchange Agreement, and an “implied transfer” will occur.

In reciprocal intent, you used resourceConformsTo property, but in examples on ValueFlows website resourceClassifiedAs is used to specify currencies. Could you explain the difference between these properties? Also, is any URI allowed?

Thanks for this question. We should probably change the examples, you could do it that way, but it is kind of a lazy shorthand. When specifying a currency, you want to be exact, and resourceConformsTo is better for that, references a ResourceSpecification and can’t have multiples, implying the most specific type object you need. Any URI is allowed, but ResourceSpecification implies that the data defined in VF can be delivered, more or less. I’m not sure how this would best work in the fediverse. And we want people to be able to use existing taxonomies too. So, hmmm, your requirements or thoughts are welcome.

The unit of measure is each but I can’t find it in OM2 vocabulary. In other examples, one is used. Which unit is preferable for currencies?

Yeah, I fudged that, it is one in OM2, but I thought it would be too confusing to explain in the example above, sorry about that. The origin of OM2 (and similar vocabs with units of measure) is scientific work, so you get this “one dimensional” concept, instead of terms more used in the economic realm. In one project, we are thinking adding something like overrideLabel for units. We’ve also thought about doing a PR to OM2 with more alternativeLabels like each, count, piece, etc.

I’m wondering, how negotiation could be represented? In some cases Agreement can be finalized only by the Seller. Perhaps Buyer should first send Create(Commitment) activity, or even Commit(), which can be accepted or rejected by Seller?

Valueflows hasn’t standardized negotiation (yet?). Your suggestion seems reasonable to me. And could be extended if even more stereotyped negotiation is needed, something like this. Or not, of course.

On all of this, your thoughts are very welcome. And if you end up doing a marketplace app, I’m happy to help work through this along the way.

1 Like

Is it correct that effortQuantity can only be used with work action, and if deliverService is used, there should be resourceQuantity? I’m trying to understand this table: https://www.valueflo.ws/assets/vf-actions.png.

Yes, I was thinking about using existing taxonomies. For example, people might want to use some kind of URN to classify resources.

Thanks!

Yes. A little background: The only reason we have 2 quantities is that use could have both effort and resource quantities, like “I need 10 drills for 8 hours” - a tradeoff between completeness for edge cases and simplicity. People usually quantify work in time-based units, although I guess they wouldn’t have to, maybe they have small, medium, or large tasks or something. But that would still be effort.

More background: the purpose for the table is to define what is needed in an app if it wants to be data-driven from the Actions, which it doesn’t have to be. The Action Event Behaviors are more for what might be wanted on an input form. The Action Resource Effect Behaviors are more for what effect an economic event should have on a resource, for inventory or maybe accounting purposes.

Possibly more than you wanted to know… :slightly_smiling_face:

Yes, I was thinking about using existing taxonomies. For example, people might want to use some kind of URN to classify resources.

Nice, will be interested to see how that works, if this app happens!

Valueflows hasn’t standardized negotiation (yet?).

I should add that negotiation seems like one of the many places that social and economic networking intersect. So maybe the sequence of messages (a thread? of commit/accept/reject/note kind of things hooked together by inReplyTo) get attached to the object being negotiated?

1 Like

I was imagining an activity like this:

{
        "type": "Create",
        "actor": "https://pub.example/bob",
        "object": {
            "type": "vf:Commitment",
            "vf:satisfies": "https://alice.example/intent/11",  # Bike
            "vf:resourceQuantity": {
                "om2:hasNumericalValue": "2",
                "om2:hasUnit": "count"
             }
        }
    }

Here Bob is saying: “I want to buy two bikes”.
Alice would then create an Agreement with requested resourceQuantity and send it back to Bob.

Maybe something like Offer with inReplyTo pointing to Intent would look less awkward.