spec.bs 171 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898
  1. <pre class='metadata'>
  2. Status: LS-BRANCH
  3. Group: ForgeFed Working Group
  4. Editor: Alice Doe, MyCoolCompany https://mycoolcompany.com/home, https://alicedoe.me/about
  5. Editor: Bob Doe, SomeOrg https://some.org, https://home.net/bob
  6. Favicon: /img/favicon.png
  7. Logo: /img/logo.svg
  8. Status Text: Work in progress
  9. Issue Tracking: Codeberg https://codeberg.org/forgefed/forgefed/issues
  10. Issue Tracker Template: https://codeberg.org/forgefed/forgefed/issues/{0}
  11. Indent: 4
  12. Work Status: exploring
  13. Repository: https://codeberg.org/forgefed/forgefed forgefed
  14. Line Numbers: yes
  15. Markup Shorthands: markdown yes, css no, http no, idl no, markup no
  16. Title: ForgeFed
  17. Shortname: forgefed
  18. URL: https://forgefed.org/spec
  19. Revision: 1
  20. Abstract:
  21. This document describes the ForgeFed vocabulary. It's intended to be an
  22. extension of [[ActivityStreams-Vocabulary]] and provides additional
  23. vocabulary for federation of project management and version control system
  24. hosting and collaboration platforms.
  25. This document describes the rules and guidelines for representing version
  26. control and project management related objects as linked data, using the
  27. ForgeFed vocabulary, ActivityStreams 2, and other related vocabularies.
  28. This document provides instructions for using ActivityPub activities and
  29. properties to represent forge events, and describes the side-effects these
  30. activities should have.
  31. </pre>
  32. Issue: (fr33) In the abstract I just pasted the 3 abstracts of the separate
  33. specs. Write something new, probably with inspiration from existing specs such
  34. as ActivityPub.
  35. This draft is generated from branch <code>[GITBRANCH]</code>, commit
  36. <a href="https://codeberg.org/ForgeFed/ForgeFed/commit/[GITCOMMIT]">[GITSHORT]</a>.
  37. # Introduction # {#intro}
  38. Note: The spec is still under construction. Want to start implementing? Check
  39. out the ActivityPub implementation guide, and then the [[#s2s]] section here.
  40. Issue: Below are the 3 intro texts from the 3 specs. We probably want to
  41. replace them with a human-friendly tutorial-like example, like in the
  42. ActivityPub spec.
  43. The ForgeFed Vocabulary describes a set of types and properties to be used by
  44. platforms that support the ForgeFed protocol. This specification describes only
  45. the new vocabulary called ForgeFed. The ForgeFed behavior specification
  46. describes how to use this vocabulary, along with standard ActivityPub
  47. vocabulary, to support the ForgeFed protocol.
  48. **The ForgeFed modeling specification** is a set of rules and guidelines which
  49. describe version control repository and project management related objects and
  50. properties, and specify how to represent them as JSON-LD objects (and linked
  51. data in general) using the ForgeFed vocabulary and related vocabularies and
  52. ontologies. Using these modeling rules consistently across implementations and
  53. instances allows to have a common language spoken across networks of software
  54. forges, project management apps and more.
  55. The ForgeFed vocabulary specification defines a dedicated vocabulary of
  56. forge-related terms, and the **modeling specification** uses these terms, along
  57. with terms that already exist in ActivityPub or elsewhere and can be reused for
  58. forge federation.
  59. The ForgeFed behavior specification provides instructions for using Activities,
  60. and which Activities and properties to use, to represent forge events, and
  61. describes the side-effects these Activities should have. The objects used as
  62. inputs and outputs of behavior descriptions there are defined here in the
  63. **modeling specification**.
  64. **The ForgeFed behavior specification** is a set of instructions for
  65. representing version control systems and project management related transactions
  66. using ActivityPub activity objects, and it describes the side effects and
  67. expected results of sending and receiving these activities. The vocabulary for
  68. these activities includes standard ActivityPub terms, new terms defined by
  69. ForgeFed, and terms borrowed from other external vocabularies.
  70. The ForgeFed vocabulary specification defines a dedicated vocabulary of
  71. forge-related terms, and the **behavior specification** uses these terms, along
  72. with terms that already exist in ActivityPub or elsewhere and can be reused for
  73. forge federation.
  74. The ForgeFed modeling specification defines rules for representing forge
  75. related objects as ActivityPub JSON-LD objects, and these objects are used in
  76. the **behavior specification**, included in activities, mentioned in
  77. activities, or modified as a result of activity side-effects.
  78. # Objects
  79. ## Kinds of Objects
  80. Objects are the core concept around which both ActivityPub and ForgeFed are
  81. built. Examples of Objects are [=Note=], [=Ticket=], [=Image=],
  82. [=Create=], [=Push=]. Some objects are resources, which are objects that
  83. contain or represent information and user made or program made content, and
  84. some objects are helpers that exist as implementation detail aren't necessarily
  85. exposed to humans or are useful to humans. But everything is an [=Object=],
  86. represented as compacted JSON-LD.
  87. ForgeFed is an ActivityPub extension, and communication between ForgeFed
  88. implementations occurs using activity objects sent to actor inboxes and
  89. outboxes.
  90. There are 4 kinds of objects in ForgeFed:
  91. : Activities
  92. :: These are objects that describe actions, such as actions that
  93. happened, actions that are happening, or a request to perform an action.
  94. Their primary use is for server-to-server interaction between actors by
  95. being sent to an actor's inbox, and client-to-server interaction between a
  96. person or program and an actor they control by being sent to the actor's
  97. outbox. Activities can also appear or be linked inside other objects and
  98. activities and be listed in Collections.
  99. : Actors
  100. :: These are static persistent objects that have an [=inbox=] and can be
  101. directly interacted with by POSTing activities to it. Their primary use is
  102. to contain or represent information and output of user actions or program
  103. actions, and to manage access to this information and modifications of it.
  104. : Child objects
  105. :: These are persistent objects that, like actors, contain or
  106. represent information and output of user actions or program actions, but
  107. they do not have their own [=inbox=] and are not directly interacted with.
  108. A managed static object always has a parent object, which is an actor, and
  109. that actor's inbox is the way to interact with the child object. The parent
  110. actor manages access and modification of the child object.
  111. : Global helper objects
  112. :: These are objects that do not belong to any actor and do not need any
  113. interaction through activities. As such, they do not exactly fit into the
  114. actor model, but may be involved in implementation details and practical
  115. considerations.
  116. Actors, children, and globals are referred to in ForgeFed as *static* objects,
  117. while activities are *dynamic* objects. The terms *constant* and *variable*
  118. are used to indicate whether an object changes during its lifetime or not.
  119. *Static* objects, in addition to being an actor or child or global, also have a
  120. resource/helper distinction:
  121. : Resource
  122. :: Contains or represents information and user made or program made
  123. content, usually belongs to the domain model of version control systems and
  124. project management.
  125. : Helper
  126. :: Used for running things behind the scenes, not exposed directly as
  127. user content, may be transient or auto generated, usually related to
  128. implementation detail and not to concepts of version control and project
  129. management.
  130. ## Object Publishing and Hosting ## {#publishing}
  131. In ForgeFed, actors host their child objects locally, meaning the actor and the
  132. child object are hosted on the same instance. Actors may create remote objects by
  133. *offering* them to the relevant actor, which then may create the object on their
  134. side and assign it a URI.
  135. The process begins with an [=Offer=] activity, in which:
  136. - [=object=] MUST be the object being offered for publishing, and that object
  137. MUST NOT have an [=id=]
  138. - [=target=] MUST indicate under which list/collection/context the sender would
  139. like the object to be published (it may also be the URI of the target actor
  140. itself)
  141. Among the recipients listed in the [=Offer=]'s recipient fields, exactly one
  142. recipient is the actor who is responsible for inspecting and possibly publishing
  143. the newly created object, and possibly sending back an [=Accept=] or a [=Reject=].
  144. We'll refer to this actor as the *target actor*. Specific object types described
  145. throughout this specification have a specific meaning for the *target actor*,
  146. which processing and inspection it is expected to do, and where it is expected
  147. to list the URI of the object once it publishes it.
  148. The sender is essentially asking that the target actor hosts the object as a
  149. child object and assigns is a URI, allowing to observe and interact with the
  150. object. The target actor will be responsible for hosting and controlling the
  151. object, and the sender will just be mentioned as the author.
  152. When an actor *A* receives the [=Offer=] activity, they can determine whether
  153. they're the *target actor* as follows: If the [=target=] is *A* or a child
  154. object of *A*, then *A* is the *target actor*. Otherwise, *A* isn't the target
  155. actor.
  156. In the following example, Luke wants to open a ticket under Aviva's Game Of
  157. Life simulation app:
  158. <div class=example>
  159. <xmp highlight=json-ld>
  160. {
  161. "@context": [
  162. "https://www.w3.org/ns/activitystreams",
  163. "https://forgefed.org/ns"
  164. ],
  165. "id": "https://forge.example/luke/outbox/02Ljp",
  166. "type": "Offer",
  167. "actor": "https://forge.example/luke",
  168. "to": [
  169. "https://dev.example/aviva/game-of-life",
  170. "https://dev.example/aviva/game-of-life/team",
  171. "https://dev.example/aviva/game-of-life/followers"
  172. ],
  173. "object": {
  174. "type": "Ticket",
  175. "attributedTo": "https://forge.example/luke",
  176. "summary": "Test test test",
  177. "content": "<p>Just testing</p>",
  178. "mediaType": "text/html",
  179. "source": {
  180. "mediaType": "text/markdown; variant=Commonmark",
  181. "content": "Just testing"
  182. }
  183. },
  184. "target": "https://dev.example/aviva/game-of-life"
  185. }
  186. </xmp>
  187. </div>
  188. The *target actor* SHOULD send an [=Accept=] or a [=Reject=] activity to the
  189. Offer's author in response. If the *target actor* sends an Accept, it MUST
  190. host its own copy, assigning an [=id=] to the newly published object and adding
  191. it to the expected list specified by the [=Offer=]'s [=target=].
  192. If the *target actor* sends a [=Reject=], it MUST NOT add the object's [=id=]
  193. to that list. However if the *target actor* doesn't make any use of the
  194. object, it MAY choose not to send a Reject, e.g. to protect user privacy. The
  195. `Accept` or `Reject` may also be delayed, e.g. until review by a human user;
  196. that is implementation dependent, and implementations should not rely on an
  197. instant response.
  198. In the [=Accept=] activity:
  199. - [=object=] MUST be the Offer activity or its [=id=]
  200. - [=result=] MUST be specified and be the [=id=] of the new child object now
  201. hosted by the *target actor*, which is extracted from the [=Offer=]'s
  202. [=object=]
  203. In the following example, Luke's ticket is opened automatically and Aviva's
  204. Game Of Life repository, which is an actor, automatically sends Luke an Accept
  205. activity:
  206. <div class=example>
  207. <xmp highlight=json-ld>
  208. {
  209. "@context": [
  210. "https://www.w3.org/ns/activitystreams",
  211. "https://forgefed.org/ns"
  212. ],
  213. "id": "https://dev.example/aviva/game-of-life/outbox/096al",
  214. "type": "Accept",
  215. "actor": "https://dev.example/aviva/game-of-life",
  216. "to": [
  217. "https://forge.example/luke",
  218. "https://dev.example/aviva/game-of-life/team",
  219. "https://dev.example/aviva/game-of-life/followers"
  220. ],
  221. "object": "https://forge.example/luke/outbox/02Ljp",
  222. "result": "https://dev.example/aviva/game-of-life/issues/113"
  223. }
  224. </xmp>
  225. </div>
  226. # Server to Server Interactions # {#s2s}
  227. Issue: This section describes the whole flow of actor interactions, that allows
  228. the federated implementation of the various features of forges and related
  229. software. It provides a complete picture of interaction flows, which the actor
  230. API section can't, because it focuses on a single actor type at a time.
  231. ## Reporting Pushed Commits ## {#pushing}
  232. - Role required for pushing: [=write=]
  233. - Role required for reporting a Push: None (because the [=Repository=] itself
  234. is publishing the Push)
  235. The ForgeFed [=Push=] activity can be used for representing an action
  236. of pushing commits into a [=Repository=]. Two actors are
  237. involved in the process, the *pusher* (usually a person) and the *repository*,
  238. and they may be hosted on different instances.
  239. [=Push=] activities MUST be authored and published by the [=Repository=], not
  240. by the actor that pushed. That actor is specified in the Push's
  241. [=attributedTo=] property.
  242. Upon a successful push, a ForgeFed implementation that publishes a Push
  243. activity MUST provide the [=type=], [=actor=], [=attributedTo=] and [=target=]
  244. properties as described in [[#Push]].
  245. See example in [[#Push]].
  246. ## Opening an issue ## {#opening-issue}
  247. Minimal required role: [=report=]
  248. The first step for opening a ticket is to determine to which actor to send the
  249. ticket. We'll refer to this actor as the *ticket tracker*. Given an object
  250. *obj* against which you'd like to open a ticket (e.g. some application's source
  251. code repository), look at the [=ticketsTrackedBy=]
  252. property of *obj*.
  253. - If `ticketsTrackedBy` isn't specified, then *obj* does't declare a way to
  254. open tickets via ForgeFed.
  255. - If `ticketsTrackedBy` is specified and is set to the [=id=] of *obj*
  256. itself, that means *obj* manages its own tickets, i.e. it is the *ticket
  257. tracker* to which you'll send the ticket.
  258. - If `ticketsTrackedBy` is specified and is set to some other object, look at
  259. the [=tracksTicketsFor=] property of that other object. If the [=id=] of
  260. *obj* is listed there under `tracksTicketsFor`, then that other object is
  261. the *ticket tracker* to which you'll send the ticket. Implementations
  262. SHOULD verify this bidirectional reference between the object and the
  263. tracker, and SHOULD NOT send a ticket if the bidirectional reference isn't
  264. found.
  265. Now that we've determined the *ticket tracker*, i.e. the actor to whom we'll
  266. send the [=Ticket=], the ticket may be opened using an [=Offer=]
  267. activity in which:
  268. - [=object=] is the ticket to be opened, it's a [=Ticket=] object with fields
  269. as described in [[#Ticket]]. It MUST specify at least [=attributedTo=],
  270. [=summary=] and [=content=], and MUST NOT specify [=id=]. If it specifies a
  271. [=context=], then it MUST be identical the Offer's [=target=] described
  272. below.
  273. - [=target=] is the ticket tracker to which the actor is offering the Ticket
  274. (e.g. a repository or project etc. under which the ticket will be opened if
  275. accepted). It MUST be either an actor or a child object. If it's a child
  276. object, the actor to whom the child object belongs MUST be listed as a
  277. recipient in the Offer's [=to=] field. If it's an actor, then that actor
  278. MUST be listed in the `to` field.
  279. The *target actor* MAY then send back an Accept or Reject. The action that has
  280. been taken by the *target actor* is indicated to the ticket author as follows:
  281. - If a [=Reject=] was sent, it means the ticket hasn't been assigned an
  282. [=id=] URI by the tracker and isn't being tracked by the tracker
  283. - If an [=Accept=] was sent, it means the ticket is now tracked and hosted on
  284. the target's side
  285. In the following example, Luke wants to open a ticket under Aviva's Game Of
  286. Life simulation app:
  287. <div class=example>
  288. <xmp highlight=json-ld>
  289. {
  290. "@context": [
  291. "https://www.w3.org/ns/activitystreams",
  292. "https://forgefed.org/ns"
  293. ],
  294. "id": "https://forge.example/luke/outbox/02Ljp",
  295. "type": "Offer",
  296. "actor": "https://forge.example/luke",
  297. "to": [
  298. "https://dev.example/aviva/game-of-life",
  299. "https://dev.example/aviva/game-of-life/team",
  300. "https://dev.example/aviva/game-of-life/followers"
  301. ],
  302. "object": {
  303. "type": "Ticket",
  304. "attributedTo": "https://forge.example/luke",
  305. "summary": "Test test test",
  306. "content": "<p>Just testing</p>",
  307. "mediaType": "text/html",
  308. "source": {
  309. "mediaType": "text/markdown; variant=Commonmark",
  310. "content": "Just testing"
  311. }
  312. },
  313. "target": "https://dev.example/aviva/game-of-life"
  314. }
  315. </xmp>
  316. </div>
  317. Luke's ticket is opened automatically and Aviva's Game Of Life repository,
  318. which is an actor, automatically sends Luke an Accept activity:
  319. <div class=example>
  320. <xmp highlight=json-ld>
  321. {
  322. "@context": [
  323. "https://www.w3.org/ns/activitystreams",
  324. "https://forgefed.org/ns"
  325. ],
  326. "id": "https://dev.example/aviva/game-of-life/outbox/096al",
  327. "type": "Accept",
  328. "actor": "https://dev.example/aviva/game-of-life",
  329. "to": [
  330. "https://forge.example/luke",
  331. "https://dev.example/aviva/game-of-life/team",
  332. "https://dev.example/aviva/game-of-life/followers"
  333. ],
  334. "object": "https://forge.example/luke/outbox/02Ljp",
  335. "result": "https://dev.example/aviva/game-of-life/issues/113"
  336. }
  337. </xmp>
  338. </div>
  339. ## Opening a merge request ## {#opening-mr}
  340. Minimal required role: [=report=]
  341. If actor *A* wishes to submit a Merge Request (MR)/Pull Request (PR)/patch
  342. against a [=Repository=] *R*, it may do so by following these
  343. steps:
  344. 1. Look at *R*'s [=sendPatchesTo=] property: That is the [=PatchTracker=] to
  345. which the MR needs to be submitted; let's call it *P*
  346. 2. Verify that *P* consents to handling MRs for repository *R* by verifying
  347. that *R* is listed in *P*'s [=tracksPatchesFor=] property
  348. 3. Publish and deliver, at least to *P*, an [=Offer=] activity in which:
  349. - [=actor=] is *A*
  350. - [=target=] is *P*
  351. - [=object=] is a [=Ticket=] in which:
  352. * [=id=] isn't specified
  353. * [=type=] is [=Ticket=]
  354. * [=attributedTo=] is *A*
  355. * [=summary=] is a one-line HTML-escaped plain-text title of the MR
  356. * [=source=] is the MR's description
  357. * [=content=] is an HTML rendering of the MR's description
  358. * [=context=], if specified, is *P*
  359. * Among the [=attachment=]s there's exactly one of type [=Offer=], in
  360. which:
  361. + [=type=] is [=Offer=]
  362. + [=origin=] is the [=Repository=] or [=Branch=] from which the
  363. proposed changes are proposed to be merged into the target
  364. repository/branch
  365. + [=target=] is the [=Repository=] or [=Branch=] into which the
  366. changes are proposed to be merged
  367. + [=object=] is an [=OrderedCollection=] of [=Patch=] objects in
  368. reverse chronological order, all of them with:
  369. - the same [=mediaType=]
  370. - that [=mediaType=] MUST match the Version Control System of
  371. the target [=Repository=]
  372. - [=attributedTo=] MUST be *A*
  373. + At least [=origin=] or [=object=] MUST be provided, both MAY be
  374. provided
  375. Actor *P* MAY send back an [=Accept=] or [=Reject=]. The action that has been
  376. taken by *P* is indicated to actor *A* as follows:
  377. - If a [=Reject=] was sent, it mean the MR has been rejected, and isn't being
  378. tracked by *P*
  379. - If an [=Accept=] was sent, it means the MR is now tracked by *P*, and its
  380. [=id=] is indicated by the [=Accept=]'s [=result=]
  381. In the following example, Luke wants to open a Merge Request against a Game Of
  382. Life simulation app:
  383. <div class=example>
  384. <xmp highlight=json-ld>
  385. {
  386. "@context": [
  387. "https://www.w3.org/ns/activitystreams",
  388. "https://forgefed.org/ns"
  389. ],
  390. "id": "https://forge.example/luke/outbox/uCSW6urN",
  391. "type": "Offer",
  392. "actor": "https://forge.example/luke",
  393. "to": [
  394. "https://dev.example/projects/game-of-life/pr-tracker"
  395. ],
  396. "cc": [
  397. "https://dev.example/projects/game-of-life",
  398. "https://dev.example/projects/game-of-life/followers",
  399. "https://dev.example/projects/game-of-life/repo",
  400. "https://dev.example/projects/game-of-life/repo/followers",
  401. "https://dev.example/projects/game-of-life/pr-tracker/followers"
  402. ],
  403. "object": {
  404. "type": "Ticket",
  405. "attributedTo": "https://forge.example/luke",
  406. "summary": "Fix the animation bug",
  407. "content": "<p>Please review, thanks!</p>",
  408. "mediaType": "text/html",
  409. "source": {
  410. "mediaType": "text/markdown; variant=Commonmark",
  411. "content": "Please review, thanks!"
  412. },
  413. "attachment": {
  414. "type": "Offer",
  415. "origin": {
  416. "type": "Branch",
  417. "context": "https://forge.example/luke/game-of-life",
  418. "ref": "refs/heads/fix-animation-bug"
  419. },
  420. "target": {
  421. "type": "Branch",
  422. "context": "https://dev.example/projects/game-of-life/repo",
  423. "ref": "refs/heads/main"
  424. },
  425. "object": {
  426. "type": "OrderedCollection",
  427. "totalItems": 1,
  428. "items": [
  429. {
  430. "type": "Patch",
  431. "attributedTo": "https://forge.example/luke",
  432. "mediaType": "application/x-git-patch",
  433. "content": "From c9ae5f4ff4a330b6e1196ceb7db1665bd4c1..."
  434. }
  435. ]
  436. }
  437. }
  438. },
  439. "target": "https://dev.example/projects/game-of-life/pr-tracker"
  440. }
  441. </xmp>
  442. </div>
  443. Luke's MR is opened automatically and the [=PatchTracker=]
  444. sends Luke an Accept activity:
  445. <div class=example>
  446. <xmp highlight=json-ld>
  447. {
  448. "@context": [
  449. "https://www.w3.org/ns/activitystreams",
  450. "https://forgefed.org/ns"
  451. ],
  452. "id": "https://dev.example/projects/game-of-life/pr-tracker/outbox/qQfFKwJ8",
  453. "type": "Accept",
  454. "actor": "https://dev.example/projects/game-of-life/pr-tracker",
  455. "to": [
  456. "https://forge.example/luke"
  457. ],
  458. "cc": [
  459. "https://dev.example/projects/game-of-life",
  460. "https://dev.example/projects/game-of-life/followers",
  461. "https://dev.example/projects/game-of-life/repo",
  462. "https://dev.example/projects/game-of-life/repo/followers",
  463. "https://dev.example/projects/game-of-life/pr-tracker/followers"
  464. ],
  465. "object": "https://forge.example/luke/outbox/uCSW6urN",
  466. "result": "https://dev.example/projects/game-of-life/pr-tracker/pulls/1219"
  467. }
  468. </xmp>
  469. </div>
  470. ## Commenting ## {#commenting}
  471. Minimal required role: [=report=]
  472. A comment on a ForgeFed resource object (such as tickets, merge requests) MUST
  473. be published as a [=Create=] activity, in which [=object=] is a [=Note=] with
  474. fields as described in [[#Comment]].
  475. In the following example, Luke replies to Aviva's comment under a merge request
  476. he submitted earlier against her Game Of Life simulation app repository:
  477. <div class=example>
  478. <xmp highlight=json-ld>
  479. {
  480. "@context": "https://www.w3.org/ns/activitystreams",
  481. "id": "https://forge.example/luke/outbox/rLaYo",
  482. "type": "Create",
  483. "actor": "https://forge.example/luke",
  484. "to": [
  485. "https://forge.example/luke/followers",
  486. "https://dev.example/aviva/game-of-life",
  487. "https://dev.example/aviva/game-of-life/followers",
  488. "https://dev.example/aviva/game-of-life/team",
  489. "https://dev.example/aviva/game-of-life/merge-requests/19/followers",
  490. "https://dev.example/aviva/game-of-life/merge-requests/19/team"
  491. ],
  492. "object": {
  493. "id": "https://forge.example/luke/comments/rD05r",
  494. "type": "Note",
  495. "attributedTo": "https://forge.example/luke",
  496. "to": [
  497. "https://forge.example/luke/followers",
  498. "https://dev.example/aviva/game-of-life",
  499. "https://dev.example/aviva/game-of-life/followers",
  500. "https://dev.example/aviva/game-of-life/team",
  501. "https://dev.example/aviva/game-of-life/merge-requests/19/followers",
  502. "https://dev.example/aviva/game-of-life/merge-requests/19/team"
  503. ],
  504. "context": "https://dev.example/aviva/game-of-life/merge-requests/19",
  505. "inReplyTo": "https://dev.example/aviva/comments/E9AGE",
  506. "mediaType": "text/html",
  507. "content": "<p>Thank you for the review! I'll submit a correction ASAP</p>",
  508. "source": {
  509. "mediaType": "text/markdown; variant=Commonmark",
  510. "content": "Thank you for the review! I'll submit a correction ASAP"
  511. },
  512. "published": "2019-11-06T20:49:05.604488Z"
  513. }
  514. }
  515. </xmp>
  516. </div>
  517. ## Granting access to shared resources ## {#managing-access}
  518. Minimal required role: [=admin=]
  519. An actor that wishes to give other specific actors access to view or modify it
  520. (or a child object of it), SHOULD do so according to the following
  521. instructions.
  522. ### Object capabilities
  523. #### Introduction #### {#s2s-grant-simple}
  524. An Object Capability (or in short OCap or OCAP) is a token providing access to
  525. certain operations on a certain resource. An actor wishing to act on a resource
  526. provides the token to the resource, alongside the Activity they wish to
  527. perform. The resource verifies the token, and if and only if it finds the token
  528. valid, and access to the requested Activity is allowed by the token, *then* the
  529. resource allows the Activity to be performed.
  530. The token provided by the actor to the resource, i.e. the OCAP, is the ID URI
  531. of a previously published [=Grant=] activity.
  532. The fundamental steps for accessing shared resources using OCAPs are:
  533. 1. The actor managing the resource (which may be the resource itself) sends a
  534. `Grant` activity to the actor to whom it wishes to grant access
  535. 2. When the actor who received the access wishes to operate on the resource,
  536. it sends the activity to the actor managing the resource, along with the ID
  537. URI of the `Grant` sent in step 1
  538. 3. The actor managing the resource verifies the access provided by the `Grant`
  539. whose ID URI is provided, and allows the activity to be performed only if
  540. the verification passes
  541. Providing the `Grant` ID URI like that when requesting to interact with a
  542. resource is called an *invocation* of the `Grant`. There is another operation
  543. possible with a `Grant` though: An actor can *delegate* a `Grant` it has
  544. received, i.e. pass on the access, giving it to more actors. Delegation is
  545. covered in a [later section](#s2s-grant-flow); for now let's assume `Grant`s
  546. are used only for invocation. We therefore get the following simplified
  547. validation process.
  548. When an actor *R* receives from actor *A* a request to access/modify a resource
  549. *r*, where the request is expressed as an activity *a* whose
  550. [=capability=] field specifies some other activity *g*, then *R*
  551. can validate *a* (i.e. decide whether or not to perform the requested action)
  552. using the following steps:
  553. 1. Resource *r* MUST be a resource that *R* manages (it may be *R* itself)
  554. 2. *g*'s [=type=] MUST be [=Grant=]
  555. 3. *g*'s [=context=] MUST be *r*
  556. 4. *g*'s [=target=] MUST be *A*
  557. 5. Verify that *g*'s [=startTime=] <= now < *g*'s [=endTime=]
  558. 6. Verify that *g* doesn't specify [=delegates=]
  559. 7. *g*'s [=actor=] MUST be *R*
  560. 8. Verify that *R* indeed published *g* and considers it an active grant
  561. (i.e. *R* hasn't disabled/revoked it)
  562. 9. *checkLeaf(g):*
  563. 1. *g*'s [=allows=] MUST be [=invoke=]
  564. 2. Actor *A* SHOULD be of a [=type=] to which *R* allows to perform
  565. activity *a* on resource *r*, i.e. *A* should probably be a [=Person=],
  566. or some automated service/bot
  567. 10. Verify that the action being requested by activity *a* to perform on
  568. resource *r* is within what *R* permits for the [=Role=] specified by *g*'s
  569. [=object=]
  570. At this point, activity *a* is considered authorized, and the requested action
  571. may be performed.
  572. #### Direct Granting
  573. When an actor *R*, managing some resource *r*, wishes to allow some other actor
  574. *A* to interact with *r*, under the conditions and permissions specified by
  575. [=Role=] *p*, then actor *R* can send to actor *A* a [=Grant=] activity
  576. with the following properties:
  577. - [=actor=]: Specifies actor *R*
  578. - [=context=]: Specifies resource *r*
  579. - [=target=]: Specifies actor *A*
  580. - [=object=]: Specifies role *p*
  581. - [=startTime=]: (optional) The time at which the Grant becomes valid
  582. - [=endTime=]: (recommended) The time at which the Grant expires
  583. - [=allows=]: Specifies [=invoke=]
  584. - [=delegates=]: Not used
  585. #### Granting the delegate role #### {#grant-delegate}
  586. A special case of direct granting is *granting permission to delegate*: If role
  587. *p* is [=delegate=], then the `Grant` [=actor=] is allowing the
  588. [=target=] to delegate `Grant`s to the [=actor=], i.e. to send `Grant`s meant
  589. for delegation or `Grants` that are themselves delegations of other `Grant`s
  590. (either start a chain, or extend a chain that some other actor started). More
  591. on delegation in the next sections.
  592. When an actor *A* wishes to allow some other actor *R* to delegate `Grant`s to
  593. actor *A*, then actor *A* can send to actor *R* a [=Grant=] activity
  594. with the following properties:
  595. - [=actor=]: Specifies actor *A*
  596. - [=context=]: Specifies actor *A*
  597. - [=target=]: Specifies actor *R*
  598. - [=object=]: Specifies [=delegate=]
  599. - [=startTime=]: (optional) The time at which the Grant becomes valid
  600. - [=endTime=]: (recommended) The time at which the Grant expires
  601. - [=allows=]: Specifies [=invoke=]
  602. - [=delegates=]: Not used
  603. #### Starting a delegation chain #### {#start-grant-chain}
  604. When an actor *R*, managing some resource *r*, wishes to allow or request some
  605. other actor *A* to delegate some access-to-*r*-under-role-*p* to certain (or
  606. any) other actors that *A* knows, then actor *R* can send to actor *A* a
  607. [=Grant=] activity with the following properties:
  608. - [=actor=]: Specifies actor *R*
  609. - [=context=]: Specifies resource *r*
  610. - [=target=]: Specifies actor *A*
  611. - [=object=]: Specifies role *p*
  612. - [=startTime=]: (optional) The time at which the Grant becomes valid
  613. - [=endTime=]: (recommended) The time at which the Grant expires
  614. - [=allows=]: Specifies the conditions under which actor *A* may
  615. delegate this `Grant` (i.e. conditions under which actor *R* will consider
  616. the delegation valid when verifying the chain), and what the recipients of
  617. the delegtaions that *A* will send (which are themselves `Grant` activites)
  618. are allowed to do with these `Grants` (invoke? further delegate to certain
  619. other actors?)
  620. - [=delegates=]: Not used
  621. - [=capability=]: *(optional)* Specifies a
  622. [[#grant-delegate|delegate Grant]] previously given by *A* to *R*
  623. The following cases are supported in ForgeFed for starting a delegation chain.
  624. The term 'component' used below refers to a forge related service actor. This
  625. may be a service of a [=type=] defined in ForgeFed (such as
  626. [=Repository=], [=TicketTracker=],
  627. [=PatchTracker=]), or a service defined in some extension.
  628. 1. [=actor=] is a component, [=target=] is a [=Project=]
  629. - Scenario: A component delegates access-to-a-resource-it-manages (which is
  630. often simply itself) to a project to which the component belongs
  631. - [=allows=] value to use: [=gatherAndConvey=]
  632. - Conditions for the target project:
  633. * It SHOULD delegate the `Grant`, allowing only `gatherAndConvey`, to
  634. its own parent projects
  635. * It SHOULD delegate the `Grant`, allowing only `distribute`, to teams
  636. to which it allows to access it
  637. * It SHOULD delegate the `Grant`, allowing only `invoke`, to people and
  638. bots to which it allows to access it
  639. * It SHOULD NOT make any other delegation of this `Grant`, and SHOULD
  640. NOT invoke it
  641. 2. [=actor=] is a [=Project=], [=target=] is a parent [=Project=] of it
  642. - Scenario: A project delegates access-to-a-resource-itself to its parent
  643. project
  644. - [=allows=] value to use: Same as 1
  645. - Conditions for the target project: Same as 1
  646. 3. [=actor=] is a component, [=target=] is a [=Team=]
  647. - Scenario: A component delegates access-to-a-resource-it-manages to a team
  648. that has been approved to access the component
  649. - [=allows=] value to use: [=distribute=]
  650. - Conditions for the target team:
  651. * It SHOULD delegate the `Grant`, allowing `distribute` only, to its
  652. subteams
  653. * It SHOULD delegate the `Grant`, allowing `invoke` only, to its
  654. members
  655. * It SHOULD NOT make any other delegation of this `Grant`, and SHOULD
  656. NOT invoke it
  657. 4. [=actor=] is a [=Project=], [=target=] is a [=Team=]
  658. - Scenario: A project delegates access-to-itself to a team that has been
  659. approved to access the project
  660. - [=allows=] value to use: Same as 3
  661. - Conditions for the target project: Same as 3
  662. #### Extending a delegation chain
  663. When an actor *A* receives a [=Grant=] activity *g* where the
  664. [=target=] is *A*, and wishes to pass on the granted access to some other actor
  665. *B* (who isn't the [=actor=] of that `Grant`), then actor *A* can do so by
  666. sending to actor *B* a new `Grant` activity *h* in which:
  667. - [=actor=] is actor *A*
  668. - [=context=] (i.e. the resource) is same as *g*'s [=context=]
  669. - [=target=] is actor *B*
  670. - [=object=] (i.e. the granted role) is either *g*'s [=object=] or a
  671. lower-access role than *g*'s [=object=], i.e. provides a subset of the
  672. permissions that *g*'s [=object=] provides (the latter case is called
  673. *attenuation*)
  674. - [=startTime=]: *(optional)* The time at which this Grant becomes valid
  675. - [=endTime=]: *(recommended)* The time at which this Grant expires
  676. - [=allows=]: Specifies the conditions under which actor *B* may
  677. delegate this `Grant` (i.e. conditions under which the delegation will be
  678. considered valid when verifying the chain), and what the recipients of
  679. the delegtaions that *B* will send (which are themselves `Grant` activites)
  680. are allowed to do with these `Grants` (invoke? further delegate to certain
  681. other actors?)
  682. - [=delegates=] is activity *g*
  683. - [=capability=]: *(optional)* Specifies a
  684. [delegate Grant](#grant-delegate) previously given by *B* to *A*
  685. - [=result=]: a URI that will be used later to verify that *h* is still active
  686. and hasn't been revoked. Alternatively, an object with [=id=] and
  687. [=duration=] as described below.
  688. The [=result=] URI MUST be provided whenever extending a delegation chain. It
  689. MUST be a URI that actor *A* controls, i.e. decides what will be returned by
  690. HTTP requests to that URI. Requirements:
  691. - From the moment that actor *A* publishes activity *h*, as long as actor *A*
  692. considers *h* an active `Grant` and hasn't revoked it, any HTTP HEAD or HTTP
  693. GET request the [=result=] URI MUST return an HTTP response status 204 or 200.
  694. - If later activity *h* is revoked, or actor *A* is deleted, then from the
  695. moment that actor *A* considers *h* deactivated, any HTTP HEAD or HTTP GET
  696. request to the [=result=] URI MUST NOT return an HTTP response status in the
  697. 200-299 range. The response status SHOULD be 410 or 404.
  698. [=result=] MAY instead specify a JSON object in which:
  699. - [=id=] is the URI as described above
  700. - *(optional)* [=duration=] specifies a duration that allows the recovation URI
  701. check to be skipped, if the duration hasn't yet passed since the last check
  702. of the URI. If [=duration=] is specified, it MUST be positive and include
  703. only an integral number of seconds that is less than `2^63`, and no other
  704. component. In other words, its format is: The string "PT", then the
  705. integer, then the string "S".
  706. In the following cases, *g* is a *request* for actor *A* to extend the
  707. delegation chain, and actor *A* SHOULD extend the chain by sending `Grant`
  708. activities, as described for each case.
  709. The term 'component' used below refers to a forge related service actor. This
  710. may be a service of a [=type=] defined in ForgeFed (such as
  711. [=Repository=], [=TicketTracker=],
  712. [=PatchTracker=]), or a service defined in some extension.
  713. 1. Actor *A* is a [=Project=], AND *g*'s [=actor=] is either a
  714. [=component=] of *A* or a [=subproject=] of
  715. *A*, AND *g*'s [=allows=] is a single value
  716. [=gatherAndConvey=]
  717. - Scenario: Project *A* received some access from a component/subproject of
  718. it, and is requested to pass it on its member people, to its member
  719. teams, and to its parent projects
  720. - Requirements for extending the delegation chain:
  721. 1. For each parent project *P* of project *A*, project *A* SHOULD
  722. publish and deliver to *P* a `Grant` activity in which:
  723. - [=actor=] is project *A*
  724. - [=context=] (i.e. the resource) is same as *g*'s [=context=]
  725. - [=target=] is project *P*
  726. - [=object=] (i.e. the granted role) is either *g*'s [=object=] or
  727. a lower-access role than *g*'s [=object=]
  728. - [=allows=] is a single value [=gatherAndConvey=]
  729. - [=delegates=] is activity *g*
  730. - [=capability=]: *(optional)* Specifies a
  731. [delegate Grant](#grant-delegate) previously given by *P* to *A*
  732. - [=result=]: a URI that will be used later to verify that *h* is
  733. still active and hasn't been revoked, or a JSON object as
  734. describes above
  735. 2. For each team *T* that project *A* considers a member team with role
  736. *p*, project *A* SHOULD publish and deliver to *T* a `Grant`
  737. activity in which:
  738. - [=actor=] is project *A*
  739. - [=context=] (i.e. the resource) is same as *g*'s [=context=]
  740. - [=target=] is team *T*
  741. - [=object=] (i.e. the granted role) is the lower-access role
  742. among *g*'s [=object=] and *p*
  743. - [=allows=] is a single value [=distribute=]
  744. - [=delegates=] is activity *g*
  745. - [=capability=]: *(optional)* Specifies a
  746. [delegate Grant](#grant-delegate) previously given by *T* to *A*
  747. - [=result=]: a URI that will be used later to verify that *h* is
  748. still active and hasn't been revoked, or a JSON object as
  749. describes above
  750. 3. For each [=Person=] or automated service bot *M* (that isn't a team)
  751. that project *A* considers a member with role *p*, project *A*
  752. SHOULD publish and deliver to *M* a `Grant` activity in which:
  753. - [=actor=] is project *A*
  754. - [=context=] (i.e. the resource) is same as *g*'s [=context=]
  755. - [=target=] is actor *M*
  756. - [=object=] (i.e. the granted role) is the lower-access role
  757. among *g*'s [=object=] and *p*
  758. - [=allows=] is a single value [=invoke=]
  759. - [=delegates=] is activity *g*
  760. - [=capability=]: *(optional)* Specifies a
  761. [delegate Grant](#grant-delegate) previously given by *M* to *A*
  762. - [=result=]: a URI that will be used later to verify that *h* is
  763. still active and hasn't been revoked, or a JSON object as
  764. describes above
  765. 4. Project *A* MUST NOT make any other delegations of *g*, and SHOULD
  766. NOT try to invoke it
  767. 2. Actor *A* is a [=Team=], AND *g*'s [=actor=] is either a
  768. component/[=Project=] in which *A* is a member or a
  769. parent team (see [=subteams=]) of *A*, AND *g*'s [=allows=] is a
  770. single value [=distribute=]
  771. - Scenario: Team *A* received some access from a component/project that
  772. considers *A* a member team, or from a parent team of *A*, and *A* is
  773. requested to pass it on its member people and to its subteams
  774. - Requirements for extending the delegation chain:
  775. 1. For each team *T* that team *A* considers a
  776. [=subteam=], team *A* SHOULD publish and deliver to *T*
  777. a `Grant` activity in which:
  778. - [=actor=] is team *A*
  779. - [=context=] (i.e. the resource) is same as *g*'s [=context=]
  780. - [=target=] is team *T*
  781. - [=object=] (i.e. the granted role) is the same as
  782. *g*'s [=object=]
  783. - [=allows=] is a single value [=distribute=]
  784. - [=delegates=] is activity *g*
  785. - [=capability=]: *(optional)* Specifies a
  786. [delegate Grant](#grant-delegate) previously given by *T* to *A*
  787. 2. For each [=Person=] or automated service bot *M* (that isn't a team)
  788. that team *A* considers a member with role *p*, team *A*
  789. SHOULD publish and deliver to *M* a `Grant` activity in which:
  790. - [=actor=] is team *A*
  791. - [=context=] (i.e. the resource) is same as *g*'s [=context=]
  792. - [=target=] is actor *M*
  793. - [=object=] (i.e. the granted role) is the lower-access role
  794. among *g*'s [=object=] and *p*
  795. - [=allows=] is a single value [=invoke=]
  796. - [=delegates=] is activity *g*
  797. - [=capability=]: *(optional)* Specifies a
  798. [delegate Grant](#grant-delegate) previously given by *M* to *A*
  799. 3. Team *A* MUST NOT make any other delegations of *g*, and SHOULD NOT
  800. try to invoke it
  801. #### Revoking a Grant #### {#s2s-revoke}
  802. At any point after an actor *A* publishes a [[#Grant]] in which it
  803. grants some actor *B* access to a resource that actor *A* manages, actor *A*
  804. MAY cancel that `Grant`, deciding it's no longer a valid OCAP to use via the
  805. [=capability=] property of activies that actor *B* sends.
  806. If actor *A* cancels such a `Grant`, it SHOULD publish and deliver, at least to
  807. actor *B*, a [=Revoke=] activity notifying about the canceled
  808. `Grant`. In the `Revoke` activity, actor *A* MUST specify the Grants being
  809. revoked, via the [=object=] property, where each Grant is specified in one of
  810. the following ways:
  811. 1. The Grant is specified by its `id` URI
  812. 2. The whole Grant activity object is provided, and MUST contain an
  813. [[fep-8b32|integrity proof]]
  814. Additional requirements:
  815. - Implementations displaying a `Revoke` activity or an interpretation of it in
  816. a human interface MUST examine the `Revoke`'s [=object=] property if it is
  817. present, check if any of the `Grant`s listed are delegations, and communicate
  818. that detail in the human interface
  819. Once actor *A* publishes the `Revoke`, it MUST from now on refuse to execute
  820. requests from actor *B* to access resources that actor *A* manages, coming as
  821. activities that specify any of the canceled `Grant`s in the `capability`
  822. property. If actor *A* receives such an activity from actor *B*, it SHOULD
  823. publish and send back a [=Reject=] activity, whose [=object=] specifies the
  824. activity that actor *B* sent.
  825. If the `Grant` that actor *A* is revoking specifies a [=result=], then from now
  826. on any HTTP HEAD request to the URI specified by [=result=] MUST NOT return an
  827. HTTP response status in the 200-299 range. The returned status SHOULD be 410
  828. or 404. See [Extending a delegation chain](#extending-a-delegation-chain) for
  829. more information.
  830. #### Verifying an invocation #### {#s2s-grant-flow}
  831. A [previous section](#s2s-grant-simple) described *direct* usage of
  832. [=Grant=]s, where the *resource actor* gives some access to a *target
  833. actor*, and the *target actor* then uses it to interact with the resource.
  834. Another way to give authorization is via delegation chains:
  835. - The *resource actor* passes access to a *target actor*, allowing (or
  836. requesting) the *target actor* to pass this access (or reduced access) on to
  837. more actors
  838. - If authorized by the delegation, those actors may further pass on the access
  839. (possibly reduced)
  840. - Eventually, an actor that received such a delegation may use it to access the
  841. resource
  842. Access is delegated using [=Grant=] activities as well, using the
  843. [=delegates=] property to point from each `Grant` in the chain to
  844. the previous one. The "direct" `Grant` discussed earlier is simply a delegation
  845. chain of length 1.
  846. When an actor *R* receives from actor *A* a request to access/modify a resource
  847. *r*, where the request is expressed as an activity *a* whose
  848. [=capability=] field specifies some other activity *g*, then *R*
  849. can validate *a* (i.e. decide whether or not to perform the requested action)
  850. using the following steps.
  851. *R* begins by verifying that resource *r* is indeed a resource that *R* manages
  852. (it may be *R* itself). Otherwise, verification has failed.
  853. *R* proceeds by collecting the delegation chain in a list, by traversing the
  854. chain backwards from the leaf all the way to the beginning of the chain. The
  855. traversal starts with the list *L* being empty, and *R* examines activity *g*:
  856. 1. *g*'s [=type=] MUST be [=Grant=]
  857. 2. *g*'s [=context=] MUST be *r*
  858. 3. *g*'s [=target=] MUST be *A*
  859. 4. *g* MUST NOT already be listed in *L*
  860. 5. Verify that *g*'s [=startTime=] <= now < *g*'s [=endTime=]
  861. 6. Look at *g*'s [=delegates=]:
  862. - If *g* doesn't specify [=delegates=]:
  863. 1. *g*'s [=actor=] MUST be *R*
  864. 2. Verify that *R* indeed published *g* and considers it an active
  865. grant (i.e. *R* hasn't disabled/revoked it)
  866. 3. Prepend *g* to the beginning of *L*, resulting with new list *M*
  867. 4. We're done with the traversal step, the output is *M*
  868. - If *g*'s [=delegates=] is some activity *h*:
  869. 1. *g*'s [=actor=] MUST NOT be *R*
  870. 2. *g* MUST specify exactly one [=result=] URI
  871. 3. Verify the [=result=]:
  872. - If it's an object with [=duration=] specified, and this duration
  873. of time hasn't yet passed since the last check, proceed without
  874. checking the URI
  875. - Otherwise, send an HTTP HEAD request to the URI, The HTTP
  876. response status MUST be 200 or 204
  877. 4. Prepend *g* to the beginning of *L*, resulting with new list *M*
  878. 5. Continue traversal by going back to step 1, but with *M* being the
  879. list, and with *g*'s [=actor=] instead of *A*, and now examining
  880. activity *h*
  881. Issue: "Going back to step 1" refers to the top-level list item; should
  882. probably tweak the CSS to display nested lists differently.
  883. *R* proceeds by traversing the resulting list *L* from the beginning forward,
  884. all the way to the leaf, validating and tracking attenuation in each step. *R*
  885. starts this by examining the first item in *L*, let's call this item *g*:
  886. 1. Let *p* be *g*'s [=object=]
  887. 2. Examine *g*'s position in *L*:
  888. - If *g* is the last item in *L*:
  889. 1. Perform *checkLeaf* on *g* (see below)
  890. 2. Verify that the action being requested by activity *a* to perform on
  891. resource *r* is within what *R* permits for [=Role=] *p*.
  892. 3. We're done with the traversal!
  893. - Otherwise:
  894. 1. Let *h* be the next item after *g* in *L*
  895. 2. Let *q* be *h*'s [=object=]
  896. 3. The permissions that role *q* allows on resource *r* MUST be
  897. identical to or a subset of the permissios that role *p* allows on
  898. *r*
  899. 4. Perform *checkItem* on *(g, h)* (see below)
  900. 5. Continue traversal by going back to step 2, but with *h* instead of
  901. *g* and *q* instead of *p*
  902. Issue: "Step 2" refers to the top-level one, need to tweak CSS for lists
  903. The steps *checkLeaf* and *checkItem* mentioned above MAY be extended by
  904. implementations, by using custom values in the [=allows=] property.
  905. But here are the standard definitions, using the values defined in ForgeFed:
  906. *checkLeaf (g):*
  907. 1. *g*'s [=allows=] MUST be [=invoke=]
  908. 2. *g*'s [=target=] (which is actor *A*, the sender of activity *a*) SHOULD be
  909. an actor of a [=type=] to which *R* allows to perform activity *a* on
  910. resource *r*, i.e. *A* should probably be a [=Person=], or some automated
  911. service/bot
  912. *checkItem (g, h):*
  913. 1. *g* MUST specify exactly one value for [=allows=]
  914. 2. That value MUST be either [=gatherAndConvey=] or [=distribute=]
  915. - If it's [=gatherAndConvey=]:
  916. 1. *g*'s [=target=] MUST be a [=Project=]
  917. - If it's [=distribute=]:
  918. 1. *g*'s [=target=] MUST be a [=Team=]
  919. 2. *h*'s [=allows=] MUST be either [=distribute=]
  920. or [=invoke=]
  921. At this point, activity *a* is considered authorized, and the requested action
  922. may be performed.
  923. #### Identifying resources and their managing actors #### {#manager}
  924. Some shared resources are themselves actors. Some shared resources aren't
  925. actors, but they are child objects of actors. When some actor *A* wishes to
  926. access a resource *R* and perform a certain operation, it needs to determine
  927. which actor to contact in order to request that operation. Actor *A* then looks
  928. at resource *R*, and the following MUST hold:
  929. - Either the resource *R* isn't an actor (i.e. doesn't have an [=inbox=]) but
  930. does specify which actor manages it via the [=managedBy=] property;
  931. - Or the resource *R* is an actor, i.e. it has an [=inbox=] (it doesn't have to
  932. specify [=managedBy=], but if it does, then it MUST refer to itself)
  933. Therefore any object that wishes to be specified as the [=context=] of a
  934. [=Grant=] MUST either be an actor or be [=managedBy=] an
  935. actor.
  936. #### Invoking a Grant
  937. Invoking a [=Grant=] means using the `Grant` to authorize a request to
  938. access or modify some resource. If some actor *A* wishes to access or modify a
  939. resource *r*, using a `Grant` activity *g* for authorization, preconditions
  940. for a successful invocation include:
  941. - *g*'s [=target=] is actor *A*
  942. - *g*'s [=context=] is either the resource *r*, or a resource in which *r* is
  943. contained, or the actor that [=managedBy|manages=] *r*
  944. - *g*'s [=object=] is a [=Role=] that permits the kind of operation
  945. that actor *A* is requesting to do on resource *r*
  946. - *g*'s [=allows=] is [=invoke=]
  947. - *g*'s [=startTime=] <= now < *g*'s [=endTime=]
  948. When actor *A* sends the activity *a* that requests to access or modify
  949. resource *r*, it can use *g* for authorization by specifying its [=id=] URI in
  950. the [=capability=] property of activity *a*.
  951. To have a chance to access resource *r*, actor *A* needs to deliver activity
  952. *a* to the actor that manages *r*. [See above](#manager) instructions for
  953. determining who that actor is.
  954. #### Time Bounds
  955. A [=Grant=] activity MUST be considered valid for invocation (or as a valid
  956. link in a delegation chain) if and only if the current time, at the time of
  957. invocation, is within the time bounds defined by the [=Grant=]:
  958. 1. A [=Grant=] MAY specify a [=startTime=]: The time at which the Grant becomes
  959. valid. If specified, the [=Grant=] is valid only if the time of invocation
  960. is equal or greater than the [=startTime=]
  961. 2. A [=Grant=] SHOULD specify an [=endTime=]: The time at which the Grant
  962. expires. If specified, the [=Grant=] is valid only if the time of
  963. invocation is less than the [=endTime=]
  964. Suggested default for picking the [=endTime=]: 6 months after publishing the
  965. [=Grant=].
  966. ### Granting access ### {#granting-access}
  967. #### Initial Grant upon resource creation
  968. When an actor *A* requests to create a new shared resource *R*, and the
  969. *resource actor* approves and creates it, then the *resource actor* SHOULD send
  970. a `Grant` to actor *A*, which provides actor *A* with access to resource *R*.
  971. The [=Role=] specified by the [=Grant=]'s [=object=] MUST be [=admin=], which
  972. means full access to *R*, including the ability to gives access-to-*R* to more
  973. actors (using an [=Invite=] activity, see below).
  974. If such a `Grant` is sent by the *resource actor* upon the creation of resource
  975. *R*, then the `Grant`'s [=fulfills=] property MUST be provided and
  976. specify the ID URI of the activity (published by actor *A*) that requested to
  977. create resource *R* (typically this would be a [=Create=] activity, see
  978. [Object Publishing and Hosting](#publishing)).
  979. If *R* is a [=Project=] or a [=Team=], additional steps occur:
  980. 1. *A*, seeing *R*'s Grant, publishes a [[#grant-delegate|delegate-Grant]]
  981. in which the [=target=] is *R* and [=capability=] is *R*'s Grant
  982. 2. From now on, whenever *R* wishes to
  983. [[#extending-a-delegation-chain|extend a Grant chain]] to *A*, it uses
  984. *A*'s delegate-Grant as the [=capability=]
  985. #### Offering access using Invite activities
  986. When an actor *A* wishes to offer actor *B* access to resource *R* (where the
  987. *resource actor* who manages *R* is neither *A* nor *B*), then actor *A* SHOULD
  988. use an [=Invite=] activity, and the following steps:
  989. 1. Actor *A* publishes and delivers an [=Invite=], at least to actor
  990. *B* and to the *resource actor* of *R*, with a relevant
  991. [=capability=] (see [[#Invite]] for details on the properties to use)
  992. 2. If actor *B* wishes to have the offered access, it publishes and delivers
  993. (at least to the *resource actor* of *R*) an [=Accept=] activity whose
  994. [=object=] specifies the `Invite` sent by actor *A*
  995. 3. The *resource actor* of *R* receives the `Invite` and the `Accept` and:
  996. 1. Verifies the `Invite` is authorized, as described above in
  997. [Verifying an invocation](#s2s-grant-flow)
  998. 2. Verifies that the `Accept`'s [=object=] specifies the `Invite` and the
  999. `Accept`'s [=actor=] is the `Invite`'s [=object=]
  1000. 3. Publishes and delivers a [=Grant=] activity (see
  1001. [[#Grant]] for more details on the properties) where:
  1002. - [=object=] is the `Invite`'s [=instrument=]
  1003. - [=context=] is the `Invite`'s [=target=], which is resource *R*
  1004. - [=target=] is the `Invite`'s [=object=], which is actor *B*
  1005. - [=fulfills=] is the `Invite`
  1006. - [=allows=] is [=invoke=]
  1007. - [=delegates=] isn't specified
  1008. 4. *B* is now considered a collaborator in *R*!
  1009. 5. If *R* is a [=Project=] or a [=Team=], additional steps occur:
  1010. 1. *B*, seeing *R*'s Grant, publishes a [[#grant-delegate|delegate-Grant]]
  1011. in which the [=target=] is *R* and [=capability=] is *R*'s Grant
  1012. 2. From now on, whenever *R* wishes to
  1013. [[#extending-a-delegation-chain|extend a Grant chain]] to *B*, it uses
  1014. *B*'s delegate-Grant as the [=capability=]
  1015. Actor *B* can now use the URI of that new `Grant` as the
  1016. [=capability=] when it sends activities that access or
  1017. manipulate resource *R*.
  1018. #### Requesting access using Join activities
  1019. When an actor *A* wishes to request access to resource *R* (where the *resource
  1020. actor* who manages *R* isn't *A*), then actor *A* SHOULD use a
  1021. [=Join=] activity, and the following steps. There are two options detailed
  1022. below, depending on whether actor *A* has been previously given a
  1023. [=Grant=] authorizing it to gain access to resource *R* without
  1024. needing someone else to approve. For example, perhaps actor *A* already has
  1025. some access to a resource collection to which *R* belongs, and that access
  1026. allows *A* to freely `Join` *R* without needing to wait for human approval.
  1027. **Option 1: Actor *A* already has a `Grant` allowing it to gain access to *R*
  1028. without external approval:**
  1029. 1. Actor *A* publishes and delivers a [=Join=], at least to the
  1030. *resource actor* of *R*, with the relevant [=capability=] it
  1031. has (see [[#Join]] for details on the properties
  1032. to use)
  1033. 2. The *resource actor* of *R* receives the `Join` and:
  1034. 1. Verifies the `Join` is authorized, as described above in
  1035. [Verifying an invocation](#s2s-grant-flow)
  1036. 2. Publishes and delivers a [=Grant=] activity (see
  1037. [[#Grant]] for more details on the
  1038. properties) where:
  1039. - [=object=] is the `Join`'s [=instrument=]
  1040. - [=context=] is the `Join`'s [=object=], which is resource *R*
  1041. - [=target=] is the `Join`'s [=actor=], which is actor *A*
  1042. - [=fulfills=] is the `Join`
  1043. 3. *A* is now considered a collaborator in *R*!
  1044. 4. If *R* is a [=Project=] or a [=Team=], additional steps occur:
  1045. 1. *A*, seeing *R*'s Grant, publishes a [[#grant-delegate|delegate-Grant]]
  1046. in which the [=target=] is *R* and [=capability=] is *R*'s Grant
  1047. 2. From now on, whenever *R* wishes to
  1048. [[#extending-a-delegation-chain|extend a Grant chain]] to *A*, it uses
  1049. *A*'s delegate-Grant as the [=capability=]
  1050. Actor *A* can now use the URI of that new `Grant` as the
  1051. [=capability=] when it sends activities that access or
  1052. manipulate resource *R*.
  1053. **Option 2: Actor *A* doesn't have (or chooses not to use) a `Grant` allowing
  1054. it to gain access to *R* without external approval:**
  1055. 1. Actor *A* publishes and delivers a [=Join=], at least to the
  1056. *resource actor* of *R* (see [[#Join]] for
  1057. details on the properties to use)
  1058. 2. If some actor *B*, that has previously received a `Grant` from the *resource
  1059. actor* of *R* authorizing it to approve joins, sees the `Join` sent by actor
  1060. *A* and decides to approve it, then actor *B* publishes and delivers (at
  1061. least to the *resource actor* of *R*) an [=Accept=] activity where:
  1062. - [=object=] specifies the `Join` sent by actor *A*
  1063. - [=capability=] is the `Grant` mentioned above,
  1064. authorizing to approve or deny Joins
  1065. 3. The *resource actor* of *R* receives the `Join` and the `Accept` and:
  1066. 1. Verifies the `Accept` is authorized, as described above in
  1067. [Verifying an invocation](#s2s-grant-flow)
  1068. 2. Verifies that the `Accept`'s [=object=] specifies the `Join`
  1069. 3. Publishes and delivers a [=Grant=] activity (see
  1070. [[#Grant]] for more details on the properties) where:
  1071. - [=object=] is the `Join`'s [=instrument=]
  1072. - [=context=] is the `Join`'s [=object=], which is resource *R*
  1073. - [=target=] is the `Join`'s [=actor=], which is actor *A*
  1074. - [=fulfills=] is the `Join`
  1075. 4. *A* is now considered a collaborator in *R*!
  1076. 5. If *R* is a [=Project=] or a [=Team=], additional steps occur:
  1077. 1. *A*, seeing *R*'s Grant, publishes a [[#grant-delegate|delegate-Grant]]
  1078. in which the [=target=] is *R* and [=capability=] is *R*'s Grant
  1079. 2. From now on, whenever *R* wishes to
  1080. [[#extending-a-delegation-chain|extend a Grant chain]] to *A*, it uses
  1081. *A*'s delegate-Grant as the [=capability=]
  1082. Actor *A* can now use the URI of that new `Grant` as the
  1083. [=capability=] when it sends activities that access or
  1084. manipulate resource *R*.
  1085. In step 2, actor *B* may choose to deny the request of actor *A*, by sending a
  1086. [=Reject=] activity (at least to the *resource actor* of *R*) where:
  1087. - [=object=] specifies the `Join` that actor *A* sent
  1088. - [=capability=] is the `Grant` mentioned in step 2, authorizing
  1089. actor *B* to approve or deny Joins
  1090. If the *resource actor* of *R* receives the `Reject`:
  1091. 1. It MUST verify the `Reject` is authorized, as described above in
  1092. [Verifying an invocation](#s2s-grant-flow)
  1093. 2. it MUST verify that the `Reject`'s [=object=] specifies the `Join`
  1094. 2. Consider this `Join` request canceled: If actor *B*, or some other actor
  1095. *C*, tries again to `Accept` the `Join`, then:
  1096. 1. The *resource actor* MUST NOT send a `Grant` to actor *A*, even if the
  1097. `Accept` is authorized
  1098. 2. The *resource actor* MAY publish and deliver a `Reject` activity, at
  1099. least to the actor that sent the `Accept`, where [=object=] specifies
  1100. the `Accept`
  1101. 4. It SHOULD publish and deliver a `Reject` activity, at least to actor *A*,
  1102. where [=object=] specifies the `Join` that actor *A* sent
  1103. So, once a `Join` is rejected (using an authorized `Reject`), it cannot be
  1104. accepted. But actor *A* MAY send a new `Join`, which could then possibly get
  1105. accepted.
  1106. ### Revoking access
  1107. #### Taking away access using Remove activities
  1108. When an actor *A* wishes to cancel the membership of another actor *B* (who
  1109. isn't *A*) in a shared resource *R*, invalidating any active
  1110. [[#Grant]]s that the *resource actor* of *R* has granted to actor
  1111. *B*, then actor *A* SHOULD use a [=Remove=] activity, and the following steps:
  1112. 1. Actor *A* publishes and delivers a [=Remove=], at least to actor
  1113. *B* and to the *resource actor* of *R*, with a relevant
  1114. [=capability=] (see [[#Remove]]
  1115. for details on the properties to use)
  1116. 2. The *resource actor* of *R* receives the `Remove` and:
  1117. 1. Verifies the `Remove` is authorized, as described above in
  1118. [Verifying an invocation](#s2s-grant-flow)
  1119. 2. Verifies that actor *B* indeed has active `Grant`s for accessing
  1120. resource *R*
  1121. 3. Marks those Grants as disabled in its internal state
  1122. 4. Publishes and delivers a [[#Revoke]] activity, as described
  1123. above in [Revoking a Grant](#s2s-revoke), where
  1124. [=fulfills=] specifies the `Remove`
  1125. Actor *B* SHOULD no longer use the URI of any `Grant` that has been disabled as
  1126. the [=capability=] when it sends activities that access or
  1127. manipulate resource *R*.
  1128. #### Waiving access using Leave activities
  1129. When an actor *A* wishes to cancel their membership in a shared resource *R*
  1130. (where the *resource actor* who manages *R* isn't *A*), invalidating any active
  1131. [[#Grant]]s that the *resource actor* of *R* has granted to actor
  1132. *A*, then actor *A* SHOULD use a [=Leave=] activity, and the following steps:
  1133. 1. Actor *A* publishes and delivers a [=Leave=], at least to the
  1134. *resource actor* of *R* (see [[#Leave]] for
  1135. details on the properties to use)
  1136. 2. The *resource actor* of *R* receives the `Leave` and:
  1137. 1. Verifies that actor *A* indeed has active `Grant`s for accessing
  1138. resource *R*
  1139. 2. Marks those Grants as disabled in its internal state
  1140. 3. Publishes and delivers a [[#Revoke]] activity, as described
  1141. above in [Revoking a Grant](#s2s-revoke), where
  1142. [=fulfills=] specifies the `Leave`
  1143. Actor *A* SHOULD no longer use the URI of any `Grant` that has been disabled as
  1144. the [=capability=] when it sends activities that access or
  1145. manipulate resource *R*.
  1146. #### Requesting to disable specific Grants using Undo
  1147. When an actor *A* wishes to deactivate a specific [[#Grant]] activity
  1148. (or multiple `Grant`s), providing access to view or manipulate some resource
  1149. *R* (where the *resource actor* of *R* isn't *A*), then actor *A* SHOULD use an
  1150. [=Undo=] activity, and the following steps. The actor *B* to whom
  1151. access-to-resource-*R* was given by the `Grant` may be actor *A* itself, or
  1152. some other actor, as long as actor *A* is authorized by the *resource actor* of
  1153. *R* to deactivate that `Grant`.
  1154. NOTE: Upon a successful `Undo`, if actor *B* doesn't have any active `Grants`
  1155. left, that allow access to resource *R*, then the *resource actor* of *R* MAY
  1156. remove actor *B*'s membership in *R*, or it MAY consider actor *B* a member
  1157. without access.
  1158. 1. Actor *A* publishes and delivers an [=Undo=], at least to the
  1159. *resource actor* of *R* (see [[#undo-grant]] for
  1160. details on the properties to use)
  1161. 2. The *resource actor* of *R* receives the `Undo` and:
  1162. 1. Verifies the `Undo` is authorized, as described above in
  1163. [Verifying an invocation](#s2s-grant-flow)
  1164. 2. Verifies that actor *B* indeed has all the active `Grant`s for accessing
  1165. resource *R*, that are listed as [=object=]s of the `Undo` (if more than
  1166. one `Grant` is listed, the [=target=] of all the `Grant`s MUST be
  1167. identical)
  1168. 3. Marks all of those Grants as disabled in its internal state
  1169. 4. Publishes and delivers a [[#Revoke]] activity, at least to
  1170. actors *A* and *B*, as described above in
  1171. [Revoking a Grant](#s2s-revoke), where:
  1172. - [=object=] MUST specify all the deactivated `Grant`s
  1173. - [=fulfills=] MUST specify the `Undo`
  1174. Actor *B* SHOULD no longer use the URI of any `Grant` that has been disabled as
  1175. the [=capability=] when it sends activities that access or
  1176. manipulate resource *R*.
  1177. ### Example
  1178. Aviva creates a new [=Repository=] for her 3D Tree Growth
  1179. Simulation software:
  1180. <div class=example>
  1181. <xmp highlight=json-ld>
  1182. {
  1183. "@context": [
  1184. "https://www.w3.org/ns/activitystreams",
  1185. "https://forgefed.org/ns"
  1186. ],
  1187. "id": "https://forge.community/users/aviva/outbox/oU6QGAqr-create-treesim",
  1188. "type": "Create",
  1189. "actor": "https://forge.community/users/aviva",
  1190. "to": [
  1191. "https://forge.community/users/aviva/followers"
  1192. ],
  1193. "object": {
  1194. "id": "https://forge.community/repos/treesim",
  1195. "type": "Repository",
  1196. "name": "Tree Growth 3D Simulation",
  1197. "summary": "A graphical simulation of trees growing"
  1198. }
  1199. }
  1200. </xmp>
  1201. </div>
  1202. The newly created *treesim* `Repository` automatically sends back a `Grant` to
  1203. Aviva, allowing her full access to the repo:
  1204. <div class=example>
  1205. <xmp highlight=json-ld>
  1206. {
  1207. "@context": [
  1208. "https://www.w3.org/ns/activitystreams",
  1209. "https://forgefed.org/ns"
  1210. ],
  1211. "id": "https://forge.community/repos/treesim/outbox/2NwyPWMX-grant-admin-to-aviva",
  1212. "type": "Grant",
  1213. "actor": "https://forge.community/repos/treesim",
  1214. "to": [
  1215. "https://forge.community/aviva",
  1216. "https://forge.community/aviva/followers"
  1217. ],
  1218. "object": "admin",
  1219. "context": "https://forge.community/repos/treesim",
  1220. "target": "https://forge.community/aviva",
  1221. "fulfills": "https://forge.community/users/aviva/outbox/oU6QGAqr-create-treesim",
  1222. "allows": "invoke",
  1223. "endTime": "2023-12-31T23:00:00-08:00"
  1224. }
  1225. </xmp>
  1226. </div>
  1227. Aviva can now use this `Grant`, e.g. to update the repo's description text:
  1228. <div class=example>
  1229. <xmp highlight=json-ld>
  1230. {
  1231. "@context": [
  1232. "https://www.w3.org/ns/activitystreams",
  1233. "https://forgefed.org/ns"
  1234. ],
  1235. "id": "https://forge.community/users/aviva/outbox/RmTygyuj",
  1236. "type": "Update",
  1237. "actor": "https://forge.community/users/aviva",
  1238. "to": [
  1239. "https://forge.community/users/aviva/followers",
  1240. "https://forge.community/repos/treesim",
  1241. "https://forge.community/repos/treesim/followers"
  1242. ],
  1243. "object": {
  1244. "id": "https://forge.community/repos/treesim",
  1245. "type": "Repository",
  1246. "name": "Tree Growth 3D Simulation",
  1247. "summary": "Tree growth 3D simulator for my nature exploration game"
  1248. },
  1249. "capability": "https://forge.community/repos/treesim/outbox/2NwyPWMX-grant-admin-to-aviva"
  1250. }
  1251. </xmp>
  1252. </div>
  1253. Aviva wants to keep track of events related to the *treesim* repo:
  1254. <div class=example>
  1255. <xmp highlight=json-ld>
  1256. {
  1257. "@context": "https://www.w3.org/ns/activitystreams",
  1258. "id": "https://forge.community/users/aviva/outbox/gqtpAhm2",
  1259. "type": "Follow",
  1260. "actor": "https://forge.community/users/aviva",
  1261. "to": "https://forge.community/repos/treesim",
  1262. "object": "https://forge.community/repos/treesim",
  1263. }
  1264. </xmp>
  1265. </div>
  1266. Aviva can invite Luke to have access to the *treesim* repo:
  1267. <div class=example>
  1268. <xmp highlight=json-ld>
  1269. {
  1270. "@context": [
  1271. "https://www.w3.org/ns/activitystreams",
  1272. "https://forgefed.org/ns"
  1273. ],
  1274. "id": "https://forge.community/users/aviva/outbox/qfrEGqnC-invite-luke",
  1275. "type": "Invite",
  1276. "actor": "https://forge.community/users/aviva",
  1277. "to": [
  1278. "https://forge.community/aviva/followers",
  1279. "https://forge.community/repos/treesim",
  1280. "https://forge.community/repos/treesim/followers",
  1281. "https://software.site/people/luke",
  1282. "https://software.site/people/luke/followers"
  1283. ],
  1284. "instrument": "maintain",
  1285. "target": "https://forge.community/repos/treesim",
  1286. "object": "https://software.site/people/luke",
  1287. "capability": "https://forge.community/repos/treesim/outbox/2NwyPWMX-grant-admin-to-aviva"
  1288. }
  1289. </xmp>
  1290. </div>
  1291. And it appears that Luke accepts the invitation:
  1292. <div class=example>
  1293. <xmp highlight=json-ld>
  1294. {
  1295. "@context": [
  1296. "https://www.w3.org/ns/activitystreams",
  1297. "https://forgefed.org/ns"
  1298. ],
  1299. "id": "https://software.site/people/luke/activities/mEYYmt8u",
  1300. "type": "Accept",
  1301. "actor": "https://software.site/people/luke",
  1302. "to": [
  1303. "https://forge.community/aviva",
  1304. "https://forge.community/aviva/followers",
  1305. "https://forge.community/repos/treesim",
  1306. "https://forge.community/repos/treesim/followers",
  1307. "https://software.site/people/luke/followers"
  1308. ],
  1309. "object": "https://forge.community/users/aviva/outbox/qfrEGqnC-invite-luke"
  1310. }
  1311. </xmp>
  1312. </div>
  1313. Seeing the `Invite` and the `Accept`, the *treesim* repo sends Luke a `Grant`
  1314. giving him the access that Aviva offered, and which he accepted:
  1315. <div class=example>
  1316. <xmp highlight=json-ld>
  1317. {
  1318. "@context": [
  1319. "https://www.w3.org/ns/activitystreams",
  1320. "https://forgefed.org/ns"
  1321. ],
  1322. "id": "https://forge.community/repos/treesim/outbox/D5uod3pz-grant-maintainer-to-luke",
  1323. "type": "Grant",
  1324. "actor": "https://forge.community/repos/treesim",
  1325. "to": [
  1326. "https://forge.community/aviva",
  1327. "https://forge.community/aviva/followers",
  1328. "https://forge.community/repos/treesim/followers",
  1329. "https://software.site/people/luke",
  1330. "https://software.site/people/luke/followers"
  1331. ],
  1332. "object": "maintain",
  1333. "context": "https://forge.community/repos/treesim",
  1334. "target": "https://software.site/people/luke",
  1335. "fulfills": "https://forge.community/users/aviva/outbox/qfrEGqnC-invite-luke",
  1336. "allows": "invoke",
  1337. "endTime": "2023-12-31T23:00:00-08:00"
  1338. }
  1339. </xmp>
  1340. </div>
  1341. Luke can now use this `Grant`, e.g. to delete some old obsolete branch of the
  1342. *treesim* repo:
  1343. <div class=example>
  1344. <xmp highlight=json-ld>
  1345. {
  1346. "@context": [
  1347. "https://www.w3.org/ns/activitystreams",
  1348. "https://forgefed.org/ns"
  1349. ],
  1350. "id": "https://software.site/people/luke/activities/vShj2aIe",
  1351. "type": "Delete",
  1352. "actor": "https://software.site/people/luke",
  1353. "to": [
  1354. "https://forge.community/repos/treesim",
  1355. "https://forge.community/repos/treesim/followers",
  1356. "https://software.site/people/luke/followers"
  1357. ],
  1358. "object": "https://forge.community/repos/treesim/branches/fixes-for-release-0.1.3",
  1359. "origin": "https://forge.community/repos/treesim",
  1360. "capability": "https://forge.community/repos/treesim/outbox/D5uod3pz-grant-maintainer-to-luke"
  1361. }
  1362. </xmp>
  1363. </div>
  1364. Celine requests to have developer access to the *treesim* repo:
  1365. <div class=example>
  1366. <xmp highlight=json-ld>
  1367. {
  1368. "@context": [
  1369. "https://www.w3.org/ns/activitystreams",
  1370. "https://forgefed.org/ns"
  1371. ],
  1372. "id": "https://dev.online/@celine/sent/v5Qvd6bB-celine-join",
  1373. "type": "Join",
  1374. "actor": "https://dev.online/@celine",
  1375. "to": [
  1376. "https://forge.community/repos/treesim",
  1377. "https://forge.community/repos/treesim/followers",
  1378. "https://dev.online/@celine/followers"
  1379. ],
  1380. "object": "https://forge.community/repos/treesim",
  1381. "instrument": "write"
  1382. }
  1383. </xmp>
  1384. </div>
  1385. Aviva sees the `Join` request, talks with Celine and decides to approve her
  1386. request:
  1387. <div class=example>
  1388. <xmp highlight=json-ld>
  1389. {
  1390. "@context": [
  1391. "https://www.w3.org/ns/activitystreams",
  1392. "https://forgefed.org/ns"
  1393. ],
  1394. "id": "https://forge.community/users/aviva/outbox/PzRtDydu",
  1395. "type": "Accept",
  1396. "actor": "https://forge.community/users/aviva",
  1397. "to": [
  1398. "https://forge.community/repos/treesim",
  1399. "https://forge.community/repos/treesim/followers",
  1400. "https://dev.online/@celine",
  1401. "https://dev.online/@celine/followers"
  1402. ],
  1403. "object": "https://dev.online/@celine/sent/v5Qvd6bB-celine-join",
  1404. "capability": "https://forge.community/repos/treesim/outbox/2NwyPWMX-grant-admin-to-aviva"
  1405. }
  1406. </xmp>
  1407. </div>
  1408. Seeing the `Join` and the `Accept`, the *treesim* repo sends Celine a `Grant`
  1409. giving her the access that she requested, and which Aviva approved:
  1410. <div class=example>
  1411. <xmp highlight=json-ld>
  1412. {
  1413. "@context": [
  1414. "https://www.w3.org/ns/activitystreams",
  1415. "https://forgefed.org/ns"
  1416. ],
  1417. "id": "https://forge.community/repos/treesim/outbox/D5uod3pz-grant-developer-to-celine",
  1418. "type": "Grant",
  1419. "actor": "https://forge.community/repos/treesim",
  1420. "to": [
  1421. "https://forge.community/aviva",
  1422. "https://forge.community/repos/treesim/followers",
  1423. "https://dev.online/@celine",
  1424. "https://dev.online/@celine/followers"
  1425. ],
  1426. "object": "write",
  1427. "context": "https://forge.community/repos/treesim",
  1428. "target": "https://dev.online/@celine",
  1429. "fulfills": "https://dev.online/@celine/sent/v5Qvd6bB-celine-join",
  1430. "allows": "invoke",
  1431. "endTime": "2023-12-31T23:00:00-08:00"
  1432. }
  1433. </xmp>
  1434. </div>
  1435. Celine can now use this `Grant` to access the *treesim* repo.
  1436. ## Adding and Removing Team Members
  1437. Minimal required role: [=admin=]
  1438. This is done using the processes described in the [[#granting-access]] section.
  1439. In particular, once the new member *M* sends the team *T* the
  1440. [[#grant-delegate|delegate-Grant]], *T* SHOULD use it to send *M*
  1441. [[#extending-a-delegation-chain|extension-Grants]] of:
  1442. - Every authorized (by a valid delegate-Grant) Grant whose [=allows=] is
  1443. [=distribute=], that *T* has received from any of its parent teams
  1444. - Every authorized (by a valid delegate-Grant) Grant whose [=allows=] is
  1445. [=distribute=], that *T* has received from any of the projects it has
  1446. direct access to
  1447. - Every authorized (by a valid delegate-Grant) Grant whose [=allows=] is
  1448. [=distribute=], that *T* has received from any of the components it has
  1449. direct access to
  1450. ## Adding and Removing Project Members
  1451. Minimal required role: [=admin=]
  1452. This is done using the processes described in the [[#granting-access]] section.
  1453. In particular, once the new member *M* sends the project *J* the
  1454. [[#grant-delegate|delegate-Grant]], *J* SHOULD use it to send *M*
  1455. [[#extending-a-delegation-chain|extension-Grants]] of:
  1456. - Every authorized (by a valid delegate-Grant) Grant whose [=allows=] is
  1457. [=gatherAndConvey=], that *J* has received from any of its child projects
  1458. - Every authorized (by a valid delegate-Grant) Grant whose [=allows=] is
  1459. [=gatherAndConvey=], that *J* has received from any of its components
  1460. ## Associating Projects and Components
  1461. Minimal required role: [=admin=]
  1462. Adding and removing a component to/from a project each have 2 versions: The
  1463. "component side" version allows an actor to initiate the action using admin
  1464. access to the component, while the "project side" version allows to initiate
  1465. the action using admin access to the project.
  1466. Whenever authorization is mentioned below, it SHOULD be done using the
  1467. [[#s2s-grant-flow|invocation verification process]].
  1468. ### Adding a component to a project - component side
  1469. Assuming:
  1470. - A project *P*
  1471. - A component *C*
  1472. - A person with admin access to *C*, Alice
  1473. - A person with admin access to *P*, Bob
  1474. Alice wants to add component *C* to project *P*. The exchange of activities
  1475. SHOULD be as follows:
  1476. 1. Alice sends an [=Add=] activity in which:
  1477. - [=object=] is *C*
  1478. - [=target=] is the URI of *P*'s [=components=] collection
  1479. - [=instrument=] is the maximal [=Role=] that Alice would like to allow for
  1480. people (and bots) when authorizing their manipulation of *C* by their
  1481. access in *P* (normally it would be [=admin=], perhaps sometimes
  1482. [=maintain=])
  1483. - [=capability=] is the URI of a [=Grant=] that authorizes admin access to
  1484. *C*
  1485. 2. *C*, seeing and authorizing the Add, publishes an [=Accept=] where
  1486. [=object=] is the Add's URI
  1487. 3. Bob, seeing the Add and the Accept, sends an [=Accept=] where:
  1488. - [=object=] is the Add's URI
  1489. - [=capability=] is the URI of a [=Grant=] that authorizes admin access to
  1490. *P*
  1491. 4. *P*, seeing the previous 3 activities and authorizing Bob's Accept,
  1492. publishes a [[#grant-delegate|delegate-Grant]] in which the [=target=] is
  1493. *C*
  1494. 5. *C*, seeing *P*'s Grant, sends a [[#start-grant-chain|start-Grant]] where:
  1495. - [=actor=] and [=context=] specify *C*
  1496. - [=target=] specifies *P*
  1497. - [=object=] specifies the role specified in the Add's [=instrument=]
  1498. - [=allows=] is [=gatherAndConvey=]
  1499. - [=capability=] is the URI of the delegate-Grant
  1500. 6. *P*, seeing and authorizing *C*'s Grant, now
  1501. [[#extending-a-delegation-chain|extends the Grant chain]] as relevant, to
  1502. its members and parent projects
  1503. ### Removing a component from a project - component side
  1504. Assuming:
  1505. - A project *P* with a component *C*
  1506. - A person with admin access to *C*, Alice
  1507. Alice wants to ask component *C* to remove itself from project *P*. The
  1508. exchange of activities SHOULD be as follows:
  1509. 1. Alice sends a [=Remove=] activity where:
  1510. - [=object=] is *C*
  1511. - [=origin=] is the URI of *P*'s [=components=] collection
  1512. - [=tag=] is *C*
  1513. - [=capability=] is the URI of a [=Grant=] that authorizes admin access to
  1514. *C*
  1515. 2. *C*, seeing and authorizing the Remove, publishes a [=Revoke=] where
  1516. [=object=] specifies the active [[#start-grant-chain|start-Grant]] it had
  1517. sent to *P* (and *C* now considers that Grant revoked and no longer
  1518. considers itself as a component of *P*)
  1519. 3. *P*, seeing the Remove and the Revoke, publishes a [=Revoke=] where
  1520. [=object=] specifies the active [[#grant-delegate|delegate-Grant]] it had
  1521. sent to *C* (and *P* now considers that Grant revoked and no longer
  1522. considers *C* a component of it)
  1523. ### Adding a component to a project - project side
  1524. Assuming:
  1525. - A project *P*
  1526. - A component *C*
  1527. - A person with admin access to *C*, Alice
  1528. - A person with admin access to *P*, Bob
  1529. Bob wants to add component *C* to project *P*. The exchange of activities
  1530. SHOULD be as follows:
  1531. 1. Bob sends an [=Invite=] activity in which:
  1532. - [=object=] is *C*
  1533. - [=target=] is the URI of *P*'s [=components=] collection
  1534. - [=instrument=] is the maximal [=Role=] that Bob would like to allow for
  1535. people (and bots) when authorizing their manipulation of *C* by their
  1536. access in *P* (normally it would be [=admin=], perhaps sometimes
  1537. [=maintain=])
  1538. - [=capability=] is the URI of a [=Grant=] that authorizes admin access to
  1539. *P*
  1540. 2. *P*, seeing and authorizing the Invite, publishes an [=Accept=] where
  1541. [=object=] is the Invite's URI
  1542. 3. Alice, seeing the Invite and the Accept, sends an [=Accept=] where:
  1543. - [=object=] is the Invite's URI
  1544. - [=capability=] is the URI of a [=Grant=] that authorizes admin access to
  1545. *C*
  1546. 4. *C*, seeing the previous 3 activities and authorizing Alice's Accept, sends
  1547. an [=Accept=] where [=object=] is the Invite's URI
  1548. 5. *P*, seeing *C*'s Accept, publishes a [[#grant-delegate|delegate-Grant]] in
  1549. which the [=target=] is *C*
  1550. 6. *C*, seeing *P*'s Grant, sends a [[#start-grant-chain|start-Grant]] where:
  1551. - [=actor=] and [=context=] specify *C*
  1552. - [=target=] specifies *P*
  1553. - [=object=] specifies the role specified in the Invite's [=instrument=]
  1554. - [=allows=] is [=gatherAndConvey=]
  1555. - [=capability=] is the URI of the delegate-Grant
  1556. 7. *P*, seeing and authorizing *C*'s Grant, now
  1557. [[#extending-a-delegation-chain|extends the Grant chain]] as relevant, to
  1558. its members and parent projects
  1559. ### Removing a component from a project - project side
  1560. Assuming:
  1561. - A project *P* with a component *C*
  1562. - A person with admin access to *P*, Bob
  1563. Bob wants to ask project *P* to remove component *C* from it. The exchange of
  1564. activities SHOULD be as follows:
  1565. 1. Bob sends a [=Remove=] activity where:
  1566. - [=object=] is *C*
  1567. - [=origin=] is the URI of *P*'s [=components=] collection
  1568. - [=tag=] is *P*
  1569. - [=capability=] is the URI of a [=Grant=] that authorizes admin access to
  1570. *P*
  1571. 3. *P*, seeing and authorizing the Remove, publishes a [=Revoke=] where
  1572. [=object=] specifies the active [[#grant-delegate|delegate-Grant]] it had
  1573. sent to *C* (and *P* now considers that Grant revoked and no longer
  1574. considers *C* a component of it)
  1575. 2. *C*, seeing the Remove and the Revoke, publishes a [=Revoke=] where
  1576. [=object=] specifies the active [[#start-grant-chain|start-Grant]] it had
  1577. sent to *P* (and *C* now considers that Grant revoked and no longer
  1578. considers itself as a component of *P*)
  1579. # Actor Interface
  1580. Issue: This section will provide, for each actor type, a summary of the various
  1581. (1) "methods" i.e. activities it can receive as requests for action; (2)
  1582. "events", i.e. activities it sends out; (3) perhaps representation of resources
  1583. that are specific to the actor type. Right now there's a grey zone between
  1584. methods and events, because some activities are methods for the target actor
  1585. but events for any other recipient, and some events are actually sent by other
  1586. actors, which cc the target actor's followers and the target actor delivers via
  1587. inbox-forwarding and not by `Announce`ing (or custom `Forward`ing) those
  1588. activities.
  1589. ## Person ## {#person-iface}
  1590. ## Team ## {#team-iface}
  1591. ## Project ## {#project-iface}
  1592. ## Repository ## {#repo-iface}
  1593. ## TicketTracker ## {#tt-iface}
  1594. ## PatchTracker ## {#pt-iface}
  1595. # Client to Server Interactions
  1596. Issue: This section is about how a human or bot can interact with the system by
  1597. POSTing activities into the outbox of a Person (or Application/Service?) actor,
  1598. and managing notifications. It's less urgent than Server-to-Server. fr33 is
  1599. using C2S in Vervis, and will be gradually working on this part.
  1600. ForgeFed uses Activities for client to server interactions, as described by
  1601. ActivityPub. A client will send objects (eg. a Ticket) wrapped in a Activity
  1602. (eg. Create) to an actor's outbox, and in turn the server will take care of
  1603. delivery.
  1604. ## Follow Activity
  1605. The Follow activity is used to subscribe to the activities of a Repository.
  1606. The client MUST send a Follow activity to the Person's outbox. The server
  1607. in turn delivers the message to the destination inbox.
  1608. ## Push Activity
  1609. The Push activity is used to notify followers when somebody has pushed changes
  1610. to a Repository.
  1611. The client MUST send a Push activity to the Repository's outbox. The server
  1612. in turn delivers the message to the Repository followers.
  1613. # Actor and Resource Representation
  1614. Issue: This section is about representation of objects. It's possible that
  1615. actor representation will move into a separate section, as well as objects
  1616. specific to one actor type. And then this section will describe only
  1617. objects/resources used by multiple actor types, such as comments (person, issue
  1618. tracker, PR tracker) and tickets (used for both issues and PRs).
  1619. ## Comment ## {#Comment}
  1620. To represent a comment, e.g. a comment on a ticket or a merge request, use the
  1621. ActivityPub [=Note=] type.
  1622. Properties:
  1623. <pre class=simpledef>
  1624. [=type=]:
  1625. [=Note=]
  1626. [=attributedTo=]:
  1627. The author of the comment
  1628. [=context=]:
  1629. The topic of the discussion, e.g. a ticket or a merge request. It MUST be
  1630. provided.
  1631. [=inReplyTo=]:
  1632. The entity on which this comment replies. MUST be provided. If the comment
  1633. is made directly on the discussion topic, then [=inReplyTo=] MUST be
  1634. identical to [=context=]. Otherwise, set [=inReplyTo=] to the comment to
  1635. which this comment replies. In that case both comments MUST have an
  1636. identical [=context=].
  1637. [=content=], [=mediaType=], [=source=]:
  1638. The comment text, in rendered form and in source form
  1639. </pre>
  1640. <div class=example>
  1641. <xmp highlight=json-ld>
  1642. {
  1643. "@context": "https://www.w3.org/ns/activitystreams",
  1644. "id": "https://forge.example/luke/comments/rD05r",
  1645. "type": "Note",
  1646. "attributedTo": "https://forge.example/luke",
  1647. "context": "https://dev.example/aviva/game-of-life/merge-requests/19",
  1648. "inReplyTo": "https://dev.example/aviva/comments/E9AGE",
  1649. "mediaType": "text/html",
  1650. "content": "<p>Thank you for the review! I'll submit a correction ASAP</p>",
  1651. "source": {
  1652. "mediaType": "text/markdown; variant=Commonmark",
  1653. "content": "Thank you for the review! I'll submit a correction ASAP"
  1654. },
  1655. "published": "2019-11-06T20:49:05.604488Z"
  1656. }
  1657. </xmp>
  1658. </div>
  1659. ## Team Membership
  1660. Properties:
  1661. <pre class=simpledef>
  1662. [=type=]:
  1663. [=Relationship=]
  1664. [=subject=]:
  1665. A [=Team=]
  1666. [=relationship=]:
  1667. [=hasMember=]
  1668. [=object=]:
  1669. A [=Person=] who is a member of the `Team`
  1670. [=tag=]:
  1671. The role that the member has in the team
  1672. </pre>
  1673. <div class=example>
  1674. <xmp highlight=json-ld>
  1675. {
  1676. "@context": [
  1677. "https://www.w3.org/ns/activitystreams",
  1678. "https://forgefed.org/ns"
  1679. ],
  1680. "id": "https://dev.example/teams/mobilizon-dev-team/members/ThmsicTj",
  1681. "type": "Relationship",
  1682. "subject": "https://dev.example/teams/mobilizon-dev-team",
  1683. "relationship": "hasMember",
  1684. "object": "https://dev.example/people/celine",
  1685. "tag": "develop"
  1686. }
  1687. </xmp>
  1688. </div>
  1689. ## Team
  1690. Properties:
  1691. <pre class=simpledef>
  1692. [=type=]:
  1693. [=Team=]
  1694. [=name=]:
  1695. The user-given name of the team, e.g. "Gitea Development Team"
  1696. [=published=]:
  1697. The time the team was created on the server
  1698. [=summary=]:
  1699. A one-line user provided description of the project, as HTML, e.g.
  1700. `"We are creating a code hosting platform"`
  1701. [=members=]:
  1702. [=Collection=] of the members of this team
  1703. [=subteams=]:
  1704. Subteams of this team, i.e. teams whose members (and subteams) inherit the
  1705. access that this team has been granted (to projects, repositories, etc.)
  1706. [=context=]:
  1707. Parent [=Team=]s of this team, i.e. teams from which this team inherits
  1708. access to projects, components and resources, e.g. repositories, ticket
  1709. trackers (and passes them to its [=members=] and inherits them to its own
  1710. [=subteams=])
  1711. </pre>
  1712. <div class=example>
  1713. <xmp highlight=json-ld>
  1714. {
  1715. "@context": [
  1716. "https://www.w3.org/ns/activitystreams",
  1717. "https://w3id.org/security/v2",
  1718. "https://forgefed.org/ns"
  1719. ],
  1720. "id": "https://dev.example/teams/mobilizon-dev-team",
  1721. "type": "Team",
  1722. "name": "Mobilizon Development Team",
  1723. "summary": "We're creating a federated tool for organizing events!",
  1724. "members": {
  1725. "type": "Collection",
  1726. "totalItems": 3,
  1727. "items": [
  1728. { "type": "Relationship",
  1729. "subject": "https://dev.example/teams/mobilizon-dev-team",
  1730. "relationship": "hasMember",
  1731. "object": "https://dev.example/people/alice",
  1732. "tag": "admin"
  1733. },
  1734. { "type": "Relationship",
  1735. "subject": "https://dev.example/teams/mobilizon-dev-team",
  1736. "relationship": "hasMember",
  1737. "object": "https://dev.example/people/bob",
  1738. "tag": "maintain"
  1739. },
  1740. { "type": "Relationship",
  1741. "subject": "https://dev.example/teams/mobilizon-dev-team",
  1742. "relationship": "hasMember",
  1743. "object": "https://dev.example/people/celine",
  1744. "tag": "develop"
  1745. }
  1746. ]
  1747. },
  1748. "subteams": {
  1749. "type": "Collection",
  1750. "totalItems": 2,
  1751. "items": [
  1752. "https://dev.example/teams/mobilizon-backend-team",
  1753. "https://dev.example/teams/mobilizon-frontend-team"
  1754. ]
  1755. },
  1756. "context": "https://dev.example/teams/framasoft-developers",
  1757. "publicKey": {
  1758. "id": "https://dev.example/teams/mobilizon-dev-team#main-key",
  1759. "owner": "https://dev.example/teams/mobilizon-dev-team",
  1760. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  1761. },
  1762. "inbox": "https://dev.example/teams/mobilizon-dev-team/inbox",
  1763. "outbox": "https://dev.example/teams/mobilizon-dev-team/outbox",
  1764. "followers": "https://dev.example/teams/mobilizon-dev-team/followers"
  1765. }
  1766. </xmp>
  1767. </div>
  1768. ## Project ## {#Project}
  1769. Properties:
  1770. <pre class=simpledef>
  1771. [=type=]:
  1772. [=Project=]
  1773. [=name=]:
  1774. The user-given name of the project, e.g. "My cool project"
  1775. [=published=]:
  1776. The time the project was created on the server
  1777. [=summary=]:
  1778. A one-line user provided description of the project, as HTML, e.g.
  1779. "`<p>A command-line tool that does cool things</p>`"
  1780. [=ticketsTrackedBy=]:
  1781. The default ticket tracker to use when submitting a ticket to this project
  1782. (this tracker MUST be listed under the project's [=components=])
  1783. [=subprojects=]:
  1784. A [=Collection=] of the subprojects of this project
  1785. [=context=]:
  1786. The parent [=Project=](s) to which this project belongs
  1787. </pre>
  1788. <div class=example>
  1789. <xmp highlight=json-ld>
  1790. {
  1791. "@context": [
  1792. "https://www.w3.org/ns/activitystreams",
  1793. "https://forgefed.org/ns"
  1794. ],
  1795. "id": "https://dev.example/projects/wanderer",
  1796. "type": "Project",
  1797. "name": "Wanderer",
  1798. "summary": "3D nature exploration game",
  1799. "components": {
  1800. "type": "Collection",
  1801. "totalItems": 7,
  1802. "items": [
  1803. "https://dev.example/repos/opengl-vegetation",
  1804. "https://dev.example/repos/opengl-vegetation/patch-tracker",
  1805. "https://dev.example/repos/treesim",
  1806. "https://dev.example/repos/treesim/patch-tracker",
  1807. "https://dev.example/repos/wanderer",
  1808. "https://dev.example/repos/wanderer/patch-tracker",
  1809. "https://dev.example/issue-trackers/wanderer"
  1810. ]
  1811. },
  1812. "subprojects": {
  1813. "type": "Collection",
  1814. "totalItems": 2,
  1815. "items": [
  1816. "https://dev.example/projects/nature-3d-models",
  1817. "https://dev.example/projects/wanderer-fundraising"
  1818. ]
  1819. },
  1820. "ticketsTrackedBy": "https://dev.example/issue-trackers/wanderer",
  1821. "inbox": "https://dev.example/projects/wanderer/inbox",
  1822. "outbox": "https://dev.example/projects/wanderer/outbox",
  1823. "followers": "https://dev.example/projects/wanderer/followers"
  1824. }
  1825. </xmp>
  1826. </div>
  1827. ## Commit
  1828. To represent a named set of changes committed into a repository's history, use
  1829. the ForgeFed [=Commit=] type. Such a committed change set is called
  1830. e.g. a *commit* in Git, and a *patch* in Darcs.
  1831. Properties:
  1832. <pre class=simpledef>
  1833. [=type=]:
  1834. [=Commit=]
  1835. [=context=]:
  1836. The [=Repository=] that this commit belongs to
  1837. [=attributedTo=]:
  1838. The commit author; if their actor URI is unknown, it MAY be their email
  1839. address as a `mailto` URI
  1840. [=created=]:
  1841. A value of type [=dateTime=] (i.e. an ISO 8601 datetime value) specifying
  1842. the time at which the commit was written by its author
  1843. [=committedBy=]:
  1844. The entity that committed the commit's changes into their local copy of the
  1845. repo, before the commit was pushed; if their actor URI is unknown, it MAY
  1846. be their email address as a `mailto` URI
  1847. [=committed=]:
  1848. The time the commit was committed by its committer
  1849. [=hash=]:
  1850. The hash identifying the commit, e.g. the commit SHA1 hash in Git; the
  1851. patch info SHA1 hash in Darcs
  1852. [=summary=]:
  1853. The commit's one-line title as HTML-escaped plain text; if the commit title
  1854. and description are a single commit message string, then the title is the
  1855. 1st line of the commit message
  1856. [=description=]:
  1857. A JSON object with a [=mediaType=] field and a [=content=] field, where
  1858. `mediaType` SHOULD be "text/plain" and `content` is the commit's
  1859. possibly-multi-line description; if the commit title and description are a
  1860. single commit message string, then the description is everything after the
  1861. 1st line of the commit message (possibly with leading whitespace stripped)
  1862. </pre>
  1863. <div class=example>
  1864. <xmp highlight=json-ld>
  1865. {
  1866. "@context": [
  1867. "https://www.w3.org/ns/activitystreams",
  1868. "https://forgefed.org/ns"
  1869. ],
  1870. "id": "https://example.dev/alice/myrepo/commits/109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
  1871. "type": "Commit",
  1872. "context": "https://example.dev/alice/myrepo",
  1873. "attributedTo": "https://example.dev/bob",
  1874. "created": "2019-07-11T12:34:56Z",
  1875. "committedBy": "https://example.dev/alice",
  1876. "committed": "2019-07-26T23:45:01Z",
  1877. "hash": "109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
  1878. "summary": "Add an installation script, fixes issue #89",
  1879. "description": {
  1880. "mediaType": "text/plain",
  1881. "content": "It's about time people can install it on their computers!"
  1882. }
  1883. }
  1884. </xmp>
  1885. </div>
  1886. ## Branch
  1887. To represent a repository branch, use the ForgeFed [=Branch=] type.
  1888. It can be a real built-in version control system branch (such as a Git branch)
  1889. or a copy of the repo used as a branch (e.g. in Darcs, which doesn't implement
  1890. branches, and the way to have branches is to keep multiple versions of the
  1891. repo).
  1892. Properties:
  1893. <pre class=simpledef>
  1894. [=type=]:
  1895. [=Branch=]
  1896. [=context=]:
  1897. The [=Repository=] that this branch belongs to
  1898. [=name=]:
  1899. The user given name of the branch, e.g. "main"
  1900. [=ref=]:
  1901. The unique identifier of the branch within the repo, e.g. "refs/heads/main"
  1902. [=team=]:
  1903. If the branch has its own access/authority/visibility settings, this can be
  1904. a [=Collection=] of the actors who have push/edit access to the branch
  1905. </pre>
  1906. <div class=example>
  1907. <xmp highlight=json-ld>
  1908. {
  1909. "@context": [
  1910. "https://www.w3.org/ns/activitystreams",
  1911. "https://forgefed.org/ns"
  1912. ],
  1913. "id": "https://example.dev/luke/myrepo/branches/master",
  1914. "type": "Branch",
  1915. "context": "https://example.dev/luke/myrepo",
  1916. "name": "master",
  1917. "ref": "refs/heads/master"
  1918. }
  1919. </xmp>
  1920. </div>
  1921. ## Repository ## {#Repository}
  1922. To represent a version control repository, use the ForgeFed
  1923. [=Repository=] type.
  1924. Properties:
  1925. <pre class=simpledef>
  1926. [=type=]:
  1927. [=Repository=]
  1928. [=name=]:
  1929. The user given name of the repository, e.g. "My cool repo"
  1930. [=cloneUri=]:
  1931. The endpoint from which the content of the repository can be obtained via
  1932. the native protocol (Git, Hg, etc.)
  1933. [=attributedTo=]:
  1934. The actor(s) in charge of the repository, e.g. a person or an organization;
  1935. if their actor URI is unknown, it MAY be their email address as a `mailto`
  1936. URI
  1937. [=published=]:
  1938. The time the repository was created on the server
  1939. [=summary=]:
  1940. A one-line user provided description of the repository, as HTML, e.g.
  1941. "`<p>A command-line tool that does cool things</p>`"
  1942. [=team=]:
  1943. [=Collection=] of actors who have management/push access to the repository,
  1944. or the subset of them who is available and wants to be
  1945. contacted/notified/responsible on repo access related activities/requests
  1946. [=forks=]:
  1947. [=OrderedCollection=] of repositories that are forks of this repository
  1948. [=ticketsTrackedBy=]:
  1949. The ticket tracker that tracks tickets for this repository, this can be the
  1950. repository itself if it manages its own tickets
  1951. [=sendPatchesTo=]:
  1952. The actor that tracks patches for this repository, this can be the
  1953. repository itself if it manages its own patches and merge requests. For
  1954. example it may be some external tracker or service, or the user or team to
  1955. whom the repository belongs.
  1956. [=context=]:
  1957. The [=Project=](s) to which this repository belongs
  1958. </pre>
  1959. <div class=example>
  1960. <xmp highlight=json-ld>
  1961. {
  1962. "@context": [
  1963. "https://www.w3.org/ns/activitystreams",
  1964. "https://w3id.org/security/v1",
  1965. "https://forgefed.org/ns"
  1966. ],
  1967. "id": "https://dev.example/aviva/treesim",
  1968. "cloneUri": "https://dev.example/aviva/treesim.git",
  1969. "type": "Repository",
  1970. "publicKey": {
  1971. "id": "https://dev.example/aviva/treesim#main-key",
  1972. "owner": "https://dev.example/aviva/treesim",
  1973. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  1974. },
  1975. "inbox": "https://dev.example/aviva/treesim/inbox",
  1976. "outbox": "https://dev.example/aviva/treesim/outbox",
  1977. "followers": "https://dev.example/aviva/treesim/followers",
  1978. "team": "https://dev.example/aviva/treesim/team",
  1979. "ticketsTrackedBy": "https://dev.example/aviva/treesim",
  1980. "sendPatchesTo": "https://dev.example/aviva/treesim",
  1981. "name": "Tree Growth 3D Simulation",
  1982. "attributedTo": "https://example.dev/bob",
  1983. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>"
  1984. }
  1985. </xmp>
  1986. </div>
  1987. ## Push ## {#Push}
  1988. To represent an event of [=Commit=]s being pushed to a
  1989. [=Repository=], use a ForgeFed [=Push=] activity.
  1990. Properties:
  1991. <pre class=simpledef>
  1992. [=type=]:
  1993. [=Push=]
  1994. [=actor=]:
  1995. The [=Repository=] to which the push was made, and that is publishing this
  1996. Push activity
  1997. [=attributedTo=]:
  1998. The entity (person, bot, etc.) that pushed the commits
  1999. [=target=]:
  2000. The specific repo history tip onto which the commits were added, this is
  2001. either a [=Branch=] (for VCSs that have branches) or a [=Repository=] (for
  2002. VCSs that don't have branches, only a single history line). If it's a
  2003. branch, it MUST be a branch belonging to the repository specified by
  2004. [=actor=]. And if it's a repository, it MUST be identical to the one
  2005. specified by [=actor=].
  2006. [=hashBefore=]:
  2007. Repo/branch/tip hash before adding the new commits
  2008. [=hashAfter=]:
  2009. Repo/branch/tip hash after adding the new commits
  2010. [=object=]:
  2011. An [=OrderedCollection=] of the [=Commit=]s being pushed, in **reverse
  2012. chronological order**. The [=items=] (or [=orderedItems=]) property of the
  2013. collection MUST contain either the whole list of commits being pushed, or a
  2014. prefix i.e. continuous subset from the beginning of the list (therefore the
  2015. **latest** commits). [=earlyItems=] MAY be used for listing a suffix i.e.
  2016. continuous subset from the end (therefore the **earliest** commits).
  2017. </pre>
  2018. <div class=example>
  2019. <xmp highlight=json-ld>
  2020. {
  2021. "@context": [
  2022. "https://www.w3.org/ns/activitystreams",
  2023. "https://forgefed.org/ns"
  2024. ],
  2025. "id": "https://dev.example/aviva/outbox/E26bE",
  2026. "type": "Push",
  2027. "actor": "https://dev.example/aviva",
  2028. "to": [
  2029. "https://dev.example/aviva/followers",
  2030. "https://dev.example/aviva/game-of-life",
  2031. "https://dev.example/aviva/game-of-life/team",
  2032. "https://dev.example/aviva/game-of-life/followers"
  2033. ],
  2034. "context": "https://dev.example/aviva/game-of-life",
  2035. "target": "https://dev.example/aviva/game-of-life/branches/master",
  2036. "hashBefore": "017cbb00bc20d1cae85f46d638684898d095f0ae",
  2037. "hashAfter": "be9f48a341c4bb5cd79ae7ab85fbf0c05d2837bb",
  2038. "object": {
  2039. "totalItems": 2,
  2040. "type": "OrderedCollection",
  2041. "orderedItems": [
  2042. {
  2043. "id": "https://dev.example/aviva/game-of-life/commits/be9f48a341c4bb5cd79ae7ab85fbf0c05d2837bb",
  2044. "type": "Commit",
  2045. "attributedTo": "https://dev.example/aviva",
  2046. "context": "https://dev.example/aviva/game-of-life",
  2047. "hash": "be9f48a341c4bb5cd79ae7ab85fbf0c05d2837bb",
  2048. "created": "2019-12-02T16:07:32Z",
  2049. "summary": "Add widget to alter simulation speed"
  2050. },
  2051. {
  2052. "id": "https://dev.example/aviva/game-of-life/commits/fa37fe100a8b1e69933889c5bf3caf95cd3ae1e6",
  2053. "type": "Commit",
  2054. "attributedTo": "https://dev.example/aviva",
  2055. "context": "https://dev.example/aviva/game-of-life",
  2056. "hash": "fa37fe100a8b1e69933889c5bf3caf95cd3ae1e6",
  2057. "created": "2019-12-02T15:51:52Z",
  2058. "summary": "Set window title correctly, fixes issue #7"
  2059. }
  2060. ]
  2061. }
  2062. }
  2063. </xmp>
  2064. </div>
  2065. ## Ticket ## {#Ticket}
  2066. To represent a work item in a project, use the ForgeFed [=Ticket=]
  2067. type.
  2068. TODO decide on ticket categories/subtypes and update below
  2069. TODO decide on property for titles, update below
  2070. TODO properly document `history` or remove it from example
  2071. Properties:
  2072. <pre class=simpledef>
  2073. [=type=]:
  2074. [=Ticket=]
  2075. [=context=]:
  2076. The [=TicketTracker=] or [=PatchTracker=] to which this ticket belongs
  2077. [=attributedTo=]:
  2078. The actor (person, bot, etc.) who submitted the ticket
  2079. [=summary=]:
  2080. The ticket's one-line title, as HTML-escaped plain text
  2081. [=content=], [=mediaType=]:
  2082. The ticket's (possibly multi-line) detailed description text, in rendered
  2083. form
  2084. [=source=]:
  2085. Source form of the ticket's description
  2086. [=published=]:
  2087. The time the ticket submission was accepted (which may not be the same as
  2088. the time the ticket was submitted)
  2089. [=followers=]:
  2090. Collection of the followers of the ticket, actors who want to be notified
  2091. on activity related to the ticket
  2092. [=team=]:
  2093. Collection of project team members who have responsibility for work on this
  2094. ticket and want to be notified on activities related to it
  2095. [=replies=]:
  2096. Collection of direct comments made on the ticket (but not comments made *on
  2097. other* comments on the ticket)
  2098. [=dependants=]:
  2099. Collection of [=Ticket=]s which depend on this ticket
  2100. [=dependencies=]:
  2101. Collection of [=Ticket=]s on which this ticket depends
  2102. [=isResolved=]:
  2103. Whether the work on this ticket is done
  2104. [=resolvedBy=]:
  2105. If the work on this ticket is done, who marked the ticket as resolved, or
  2106. which activity did so
  2107. [=resolved=]:
  2108. When the ticket has been marked as resolved
  2109. </pre>
  2110. There's an important distinction between these two kinds of tickets:
  2111. - Task:
  2112. A work item which tracks some task to be done, in which the task and its
  2113. results are described in text, but the work itself is done elsewhere. This
  2114. is often called "issue" in software project hosting platforms. Tasks are
  2115. general-purpose work items that aren't specific to software development or
  2116. software projects.
  2117. - Merge request:
  2118. A work item that includes a proposal or request to apply some specific
  2119. changes to a specific [=Repository=]. The work requested by the work item
  2120. is to review and (decide whether to) merge the proposed patches to the
  2121. repository. This kind of work item is often called "Pull Request" or "Merge
  2122. Request" on software project hosting platforms.
  2123. ### Task / Issue
  2124. A task is represented as a [[#Ticket]] as described above, with the
  2125. following additional requirements:
  2126. - [=context=] is the [=TicketTracker=] to which this task belongs
  2127. - There is no [=attachment=] of type [=Offer=] (but there may be attachment of other types)
  2128. <div class=example>
  2129. <xmp highlight=json-ld>
  2130. {
  2131. "@context": [
  2132. "https://www.w3.org/ns/activitystreams",
  2133. "https://forgefed.org/ns"
  2134. ],
  2135. "id": "https://dev.example/aviva/game-of-life/issues/107",
  2136. "type": "Ticket",
  2137. "context": "https://dev.example/aviva/game-of-life",
  2138. "attributedTo": "https://forge.example/luke",
  2139. "summary": "Window title is empty",
  2140. "content": "<p>When I start the simulation, window title disappears suddenly</p>",
  2141. "mediaType": "text/html",
  2142. "source": {
  2143. "mediaType": "text/markdown; variant=Commonmark",
  2144. "content": "When I start the simulation, window title disappears suddenly",
  2145. },
  2146. "published": "2019-11-04T07:00:04.465807Z",
  2147. "followers": "https://dev.example/aviva/game-of-life/issues/107/followers",
  2148. "team": "https://dev.example/aviva/game-of-life/issues/107/team",
  2149. "replies": "https://dev.example/aviva/game-of-life/issues/107/discussion",
  2150. "history": "https://dev.example/aviva/game-of-life/issues/107/activity",
  2151. "dependants": "https://dev.example/aviva/game-of-life/issues/107/rdeps",
  2152. "dependencies": "https://dev.example/aviva/game-of-life/issues/107/deps",
  2153. "isResolved": true,
  2154. "resolvedBy": "https://code.example/martin",
  2155. "resolved": "2020-02-07T06:45:03.281314Z"
  2156. }
  2157. </xmp>
  2158. </div>
  2159. ### Merge Request / Pull Request
  2160. A merge request is represented as a [[#Ticket]] as described above, with
  2161. the following additional requirements:
  2162. - [=context=] is the [=PatchTracker=] to which this merge request belongs
  2163. - There is no [=attachment=] of type [=Offer=] (but there may be attachment of other types)
  2164. - There is exactly one [=attachment=] of type [=Offer=], as described below
  2165. - There MAY be more [=attachment=]s, but they MUST NOT be of type [=Offer=]
  2166. In that special [=attachment=] of type [=Offer=]:
  2167. - [=type=] is [=Offer=]
  2168. - [=origin=] is the [=Repository=] or [=Branch=] from which the proposed changes are proposed to be merged into the target repository/branch
  2169. - [=target=] is the [=Repository=] or [=Branch=] into which the changes are proposed to be merged
  2170. - [=object=] is an [=OrderedCollection=] of [=Patch=]es in reverse
  2171. chronological order, in which, in addition to standard [=OrderedCollection=]
  2172. properties:
  2173. - [=context=] is (the [=id=] of) the [[#Ticket]]
  2174. - [=previousVersions=] is a list of previous versions
  2175. of the merge request's proposed changes, i.e. previous versions of this
  2176. [=OrderedCollection=]; each of those uses
  2177. [=currentVersion=] to point back to this latest
  2178. version
  2179. <div class=example>
  2180. <xmp highlight=json-ld>
  2181. {
  2182. "@context": [
  2183. "https://www.w3.org/ns/activitystreams",
  2184. "https://forgefed.org/ns"
  2185. ],
  2186. "id": "https://dev.example/aviva/game-of-life/pulls/825",
  2187. "type": "Ticket",
  2188. "context": "https://dev.example/aviva/game-of-life",
  2189. "attributedTo": "https://forge.example/luke",
  2190. "summary": "Fix the empty window title bug",
  2191. "content": "<p>This fixes the bug making the title disappear</p>",
  2192. "mediaType": "text/html",
  2193. "source": {
  2194. "mediaType": "text/markdown; variant=Commonmark",
  2195. "content": "This fixes the bug making the title disappear",
  2196. },
  2197. "published": "2022-09-15T14:52:00.125987Z",
  2198. "followers": "https://dev.example/aviva/game-of-life/pulls/825/followers",
  2199. "replies": "https://dev.example/aviva/game-of-life/pulls/825/discussion",
  2200. "isResolved": false,
  2201. "attachment": {
  2202. "type": "Offer",
  2203. "origin": {
  2204. "type": "Branch",
  2205. "context": "https://forge.example/luke/game-of-life",
  2206. "ref": "refs/heads/fix-title-bug"
  2207. },
  2208. "target": {
  2209. "type": "Branch",
  2210. "context": "https://dev.example/aviva/game-of-life",
  2211. "ref": "refs/heads/main"
  2212. },
  2213. "object": {
  2214. "id": "https://dev.example/aviva/game-of-life/pulls/825/versions/1",
  2215. "type": "OrderedCollection",
  2216. "totalItems": 1,
  2217. "items": [
  2218. {
  2219. "type": "Patch",
  2220. "attributedTo": "https://forge.example/luke",
  2221. "context": "https://dev.example/aviva/game-of-life/pulls/825/versions/1",
  2222. "mediaType": "application/x-git-patch",
  2223. "content": "From c9ae5f4ff4a330b6e1196ceb7db1665bd4c1..."
  2224. }
  2225. ],
  2226. "context": "https://dev.example/aviva/game-of-life/pulls/825"
  2227. }
  2228. }
  2229. }
  2230. </xmp>
  2231. </div>
  2232. ## Patch
  2233. <pre class=simpledef>
  2234. [=type=]:
  2235. [=Patch=]
  2236. [=attributedTo=]:
  2237. The [=Person=] who has written the patch
  2238. [=context=]:
  2239. An [=OrderedCollection=] representing a sequence of patches, being
  2240. submitted together as a proposed change to a certain [=Repository=], and
  2241. this patch is an item in that collection
  2242. [=content=]:
  2243. A description of the changes that this patch proposes, encoded in the
  2244. format specified by [=mediaType=]
  2245. [=mediaType=]:
  2246. A native patch format used by the Version Control System of the
  2247. [=Repository=] to which the patch is proposed, and in which the [=content=]
  2248. of this patch is encoded
  2249. </pre>
  2250. <div class=example>
  2251. <xmp highlight=json-ld>
  2252. {
  2253. "@context": [
  2254. "https://www.w3.org/ns/activitystreams",
  2255. "https://forgefed.org/ns"
  2256. ],
  2257. "id": "https://dev.example/aviva/game-of-life/pulls/825/versions/1/patches/1",
  2258. "type": "Patch",
  2259. "attributedTo": "https://forge.example/luke",
  2260. "context": "https://dev.example/aviva/game-of-life/pulls/825/versions/1",
  2261. "mediaType": "application/x-git-patch",
  2262. "content": "From c9ae5f4ff4a330b6e1196ceb7db1665bd4c1..."
  2263. }
  2264. </xmp>
  2265. </div>
  2266. # Access Control
  2267. ## Giving Access
  2268. ### Invite ### {#Invite}
  2269. To offer some actor access to a shared resource (such as a repository or a
  2270. ticket tracker), use an ActivityPub [=Invite=] activity.
  2271. Properties:
  2272. <pre class=simpledef>
  2273. [=type=]:
  2274. [=Invite=]
  2275. [=actor=]:
  2276. The entity (person, bot, etc.) that is offering access
  2277. [=instrument=]:
  2278. A [=Role=] specifying which operations on the resource are being allowed
  2279. [=target=]:
  2280. The resource, access to which is being given (for example, a repository)
  2281. [=object=]:
  2282. The actor who is being gives access to the resource
  2283. [=capability=]:
  2284. A previously published `Grant`, giving the `actor` permission to invite
  2285. more actors to access the resource
  2286. </pre>
  2287. <div class=example>
  2288. <xmp highlight=json-ld>
  2289. {
  2290. "@context": [
  2291. "https://www.w3.org/ns/activitystreams",
  2292. "https://forgefed.org/ns"
  2293. ],
  2294. "id": "https://dev.example/aviva/outbox/B47d3",
  2295. "type": "Invite",
  2296. "actor": "https://dev.example/aviva",
  2297. "to": [
  2298. "https://dev.example/aviva/followers",
  2299. "https://coding.community/repos/game-of-life",
  2300. "https://coding.community/repos/game-of-life/followers",
  2301. "https://software.site/bob",
  2302. "https://software.site/bob/followers"
  2303. ],
  2304. "instrument": "maintain",
  2305. "target": "https://coding.community/repos/game-of-life",
  2306. "object": "https://software.site/bob",
  2307. "capability": "https://coding.community/repos/game-of-life/outbox/2c53A"
  2308. }
  2309. </xmp>
  2310. </div>
  2311. ### Join ### {#Join}
  2312. To request access to a shared resource, use an ActivityPub [=Join=] activity.
  2313. Properties:
  2314. <pre class=simpledef>
  2315. [=type=]:
  2316. [=Join=]
  2317. [=actor=]:
  2318. The entity (person, bot, etc.) that is requesting access
  2319. [=instrument=]:
  2320. A [=Role=] specifying which operations on the resource are being requested
  2321. [=object=]:
  2322. The resource, access to which is being given (for example, a repository)
  2323. [=capability=]:
  2324. *(optional)* A previously published `Grant`, giving the `actor` permission
  2325. to gain access to the resource without the approval of another actor. If
  2326. `capability` isn't provided, the resource won't grant access before someone
  2327. with adequate access approves the Join request.
  2328. </pre>
  2329. <div class=example>
  2330. <xmp highlight=json-ld>
  2331. {
  2332. "@context": [
  2333. "https://www.w3.org/ns/activitystreams",
  2334. "https://forgefed.org/ns"
  2335. ],
  2336. "id": "https://software.site/bob/outbox/c97E3",
  2337. "type": "Join",
  2338. "actor": "https://software.site/bob",
  2339. "to": [
  2340. "https://coding.community/repos/game-of-life",
  2341. "https://coding.community/repos/game-of-life/followers",
  2342. "https://software.site/bob/followers"
  2343. ],
  2344. "instrument": "maintain",
  2345. "object": "https://coding.community/repos/game-of-life",
  2346. "capability": "https://coding.community/repos/game-of-life/outbox/d38Fa"
  2347. }
  2348. </xmp>
  2349. </div>
  2350. ### Grant ### {#Grant}
  2351. To give some actor access to a shared resource, use a ForgeFed
  2352. [=Grant=] activity.
  2353. Properties:
  2354. <pre class=simpledef>
  2355. [=type=]:
  2356. [=Grant=]
  2357. [=actor=]:
  2358. The entity (person, bot, etc.) that is giving access
  2359. [=object=]:
  2360. A [=Role=] specifying which operations on the resource are being allowed
  2361. [=context=]:
  2362. The resource, access to which is being given (for example, a repository)
  2363. [=target=]:
  2364. The actor who is being gives access to the resource
  2365. [=fulfills=]:
  2366. The activity that triggered the sending of the `Grant`, such as a related
  2367. `Invite` (another example&#x3a; if Alice [=Create=]s a new repository, the
  2368. repository may automatically send back a [=Grant=] giving Alice admin
  2369. access, and this Grant's `fulfills` refers to the [=Create=] that Alice
  2370. sent)
  2371. [=result=]:
  2372. A URI that can be used later for verifying that the given access is still
  2373. approved, thus allowing the actor granting the access to revoke it.
  2374. Alternatively, a JSON object where [=id=] is the URI and [=duration=] MAY
  2375. be specified to allow to skip the revocation check if the duration time
  2376. hasn't yet passed since the last check. If [=duration=] is specified, it
  2377. MUST be positive, and specify only an integral number of seconds that is
  2378. less than `2^63`, and no other component.
  2379. [=allows=]:
  2380. Modes of invocation and/or delegation that this `Grant` is meant to be used
  2381. for
  2382. [=delegates=]:
  2383. If this `Grant` is a delegation, i.e. it is passing on some access that it
  2384. has received, `delegates` specifies the parent `Grant` that it has received
  2385. and now passing on
  2386. [=startTime=]:
  2387. *(optional)* The time at which the Grant becomes valid
  2388. [=endTime=]:
  2389. *(recommended)* The time at which the Grant expires
  2390. </pre>
  2391. <div class=example>
  2392. <xmp highlight=json-ld>
  2393. {
  2394. "@context": [
  2395. "https://www.w3.org/ns/activitystreams",
  2396. "https://forgefed.org/ns"
  2397. ],
  2398. "id": "https://coding.community/repos/game-of-life/outbox/9fA8c",
  2399. "type": "Grant",
  2400. "actor": "https://coding.community/repos/game-of-life",
  2401. "to": [
  2402. "https://dev.example/aviva",
  2403. "https://dev.example/aviva/followers",
  2404. "https://coding.community/repos/game-of-life/followers",
  2405. "https://software.site/bob",
  2406. "https://software.site/bob/followers"
  2407. ],
  2408. "object": "maintain",
  2409. "context": "https://coding.community/repos/game-of-life",
  2410. "target": "https://software.site/bob",
  2411. "fulfills": "https://dev.example/aviva/outbox/B47d3",
  2412. "allows": "invoke",
  2413. "endTime": "2023-12-31T23:00:00-08:00"
  2414. }
  2415. </xmp>
  2416. </div>
  2417. ## Canceling Access
  2418. ### Remove ### {#Remove}
  2419. To disable an actor's membership in a shared resource, invalidating their
  2420. access to it, use an ActivityPub [=Remove=] activity.
  2421. Properties:
  2422. <pre class=simpledef>
  2423. [=type=]:
  2424. [=Remove=]
  2425. [=actor=]:
  2426. The actor (person, bot, etc.) that is disabling access disabled
  2427. [=object=]:
  2428. The actor whose access to the resource is being taken away
  2429. [=origin=]:
  2430. The resource, access to which is being taken away (for example, a
  2431. repository)
  2432. [=capability=]:
  2433. A previously published `Grant`, giving the `actor` permission to disable
  2434. the [=object=] actor's access to the resource
  2435. </pre>
  2436. <div class=example>
  2437. <xmp highlight=json-ld>
  2438. {
  2439. "@context": [
  2440. "https://www.w3.org/ns/activitystreams",
  2441. "https://forgefed.org/ns"
  2442. ],
  2443. "id": "https://dev.example/aviva/outbox/F941b",
  2444. "type": "Remove",
  2445. "actor": "https://dev.example/aviva",
  2446. "to": [
  2447. "https://dev.example/aviva/followers",
  2448. "https://coding.community/repos/game-of-life",
  2449. "https://coding.community/repos/game-of-life/followers",
  2450. "https://software.site/bob",
  2451. "https://software.site/bob/followers"
  2452. ],
  2453. "origin": "https://coding.community/repos/game-of-life",
  2454. "object": "https://software.site/bob",
  2455. "capability": "https://coding.community/repos/game-of-life/outbox/2c53A"
  2456. }
  2457. </xmp>
  2458. </div>
  2459. ### Leave ### {#Leave}
  2460. To withdraw your consent for membership in a shared resource, invalidating
  2461. your access to it, use an ActivityPub [=Leave=] activity.
  2462. Properties:
  2463. <pre class=simpledef>
  2464. [=type=]:
  2465. [=Leave=]
  2466. [=actor=]:
  2467. The actor (person, bot, etc.) that is requesting to disable their own
  2468. access
  2469. [=object=]:
  2470. The resource, access to which is being disabled (for example, a repository)
  2471. </pre>
  2472. <div class=example>
  2473. <xmp highlight=json-ld>
  2474. {
  2475. "@context": [
  2476. "https://www.w3.org/ns/activitystreams",
  2477. "https://forgefed.org/ns"
  2478. ],
  2479. "id": "https://software.site/bob/outbox/d08F4",
  2480. "type": "Leave",
  2481. "actor": "https://software.site/bob",
  2482. "to": [
  2483. "https://coding.community/repos/game-of-life",
  2484. "https://coding.community/repos/game-of-life/followers",
  2485. "https://software.site/bob/followers"
  2486. ],
  2487. "object": "https://coding.community/repos/game-of-life"
  2488. }
  2489. </xmp>
  2490. </div>
  2491. ### Revoke ### {#Revoke}
  2492. Another activity that can be used for disabling access is [=Revoke=].
  2493. While [[#Remove]] and [[#Leave]] are meant for undoing the effects
  2494. of [[#Invite]] and [[#Join]], `Revoke` is provided as an opposite of
  2495. [[#Grant]]. See the Behavior specification for more information about the
  2496. usage of these different activity types in revocation of access to shared
  2497. resources.
  2498. Properties:
  2499. <pre class=simpledef>
  2500. [=type=]:
  2501. [=Revoke=]
  2502. [=actor=]:
  2503. The actor (person, bot, etc.) that is revoking access
  2504. [=fulfills=]:
  2505. An activity that triggered the sending of the `Revoke`, such as a related
  2506. `Remove` or `Leave`
  2507. [=object=]:
  2508. specific [[#Grant]] activities being undone, i.e. the access that they
  2509. granted is now disabled and it cannot be used anymore as the [=capability=]
  2510. of activities. Each Grant may either be mentioned by its [=id=] URI, or be
  2511. included as a full object that includes an [[fep-8b32|integrity proof]].
  2512. </pre>
  2513. <div class=example>
  2514. <xmp highlight=json-ld>
  2515. {
  2516. "@context": [
  2517. "https://www.w3.org/ns/activitystreams",
  2518. "https://forgefed.org/ns"
  2519. ],
  2520. "id": "https://coding.community/repos/game-of-life/outbox/1C0e2",
  2521. "type": "Revoke",
  2522. "actor": "https://coding.community/repos/game-of-life",
  2523. "to": [
  2524. "https://coding.community/repos/game-of-life/followers",
  2525. "https://software.site/bob",
  2526. "https://software.site/bob/followers"
  2527. ],
  2528. "object": "https://coding.community/repos/game-of-life/outbox/9fA8c"
  2529. }
  2530. </xmp>
  2531. </div>
  2532. ### Undo a Grant ### {#undo-grant}
  2533. The Behavior spec describes flows in which the [[#Revoke]] activity is
  2534. used by resources (more accurately, by the actors managing them) to announce
  2535. that they're disabling [[#Grant]]s that they previously sent. To allow for
  2536. a clear distinction, another activity is provided here, for *other* actors to
  2537. *request* the revocation of specific [[#Grant]]s: The ActivityPub [=Undo=]
  2538. activity.
  2539. It's likely that `Grant`s would exist behind-the-scenes in applications, and
  2540. human actors would then use activities such as `Remove` and `Leave` for
  2541. disabling access. But the ability to disable specific `Grant`s may be required
  2542. for ensuring and maintaining system security, therefore `Undo` is provided here
  2543. as well.
  2544. Properties:
  2545. <pre class=simpledef>
  2546. [=type=]:
  2547. [=Undo=]
  2548. [=actor=]:
  2549. The actor (person, bot, etc.) that is revoking access
  2550. [=object=]:
  2551. specific [[#Grant]] activities being undone, i.e. the access that they
  2552. granted is now disabled and it cannot be used anymore as the [=capability=]
  2553. of activities
  2554. [=capability=]:
  2555. A previously published `Grant`, giving the `actor` permission to disable
  2556. the [=object=] actor's access to the resource
  2557. </pre>
  2558. # Vocabulary # {#vocab}
  2559. ## Types
  2560. The base URI of all ForgeFed terms is `https://forgefed.org/ns#`.
  2561. The ForgeFed vocabulary has a JSON-LD context whose URI is
  2562. `https://forgefed.org/ns`. Implementers MUST either include the
  2563. ActivityPub and ForgeFed contexts in their object definitions, or other
  2564. contexts that would result with the ActivityPub and ForgeFed terms being
  2565. assigned they correct full URIs. Implementers MAY include additional contexts
  2566. and terms as appropriate.
  2567. A typical `@context` of a ForgeFed object may look like this:
  2568. <div class=example>
  2569. <xmp highlight=json-ld>
  2570. "@context": [
  2571. "https://www.w3.org/ns/activitystreams",
  2572. "https://forgefed.org/ns"
  2573. ]
  2574. </xmp>
  2575. </div>
  2576. ### Activities
  2577. #### Related to access control
  2578. <pre class=simpledef>
  2579. Name: <dfn dfn export>Grant</dfn>
  2580. URI: https://forgefed.org/ns#Grant
  2581. Extends: [=Activity=]
  2582. Description:
  2583. Indicates that [=target=] is being given (by the [=actor=]) access to a
  2584. resource specified by [=context=] under the role/permission specified by
  2585. [=object=].
  2586. </pre>
  2587. <div class=example>
  2588. <xmp highlight=json-ld line-highlight=7>
  2589. {
  2590. "@context": [
  2591. "https://www.w3.org/ns/activitystreams",
  2592. "https://forgefed.org/ns"
  2593. ],
  2594. "id": "https://example.dev/aviva/outbox/reBGo",
  2595. "type": "Grant",
  2596. "actor": "https://example.dev/aviva",
  2597. "to": [
  2598. "https://example.dev/aviva/followers",
  2599. "https://example.dev/aviva/myproject",
  2600. "https://example.dev/aviva/myproject/followers",
  2601. "https://example.dev/bob",
  2602. "https://example.dev/bob/followers"
  2603. ],
  2604. "object": "write",
  2605. "context": "https://example.dev/aviva/myproject",
  2606. "target": "https://example.dev/bob"
  2607. }
  2608. </xmp>
  2609. </div>
  2610. <pre class=simpledef>
  2611. Name: <dfn dfn export>Revoke</dfn>
  2612. URI: https://forgefed.org/ns#Revoke
  2613. Extends: [=Activity=]
  2614. Description:
  2615. Indicates that the [=actor=] is canceling [=target=]'s access to a resource
  2616. specified by [=context=] under the role specified by [=instrument=], making
  2617. the [=Grant=] activities specified by [=object=] unusable anymore in other
  2618. activities' [=capability=] field.
  2619. </pre>
  2620. <div class=example>
  2621. <xmp highlight=json-ld line-highlight=7>
  2622. {
  2623. "@context": [
  2624. "https://www.w3.org/ns/activitystreams",
  2625. "https://forgefed.org/ns"
  2626. ],
  2627. "id": "https://example.dev/myproject/outbox/nlTxb",
  2628. "type": "Revoke",
  2629. "actor": "https://example.dev/myproject",
  2630. "to": [
  2631. "https://example.dev/myproject/followers",
  2632. "https://example.dev/users/aviva"
  2633. ],
  2634. "object": "https://example.dev/myproject/outbox/reBGo",
  2635. "instrument": "https://example.dev/roles/developer",
  2636. "context": "https://example.dev/myproject",
  2637. "target": "https://example.dev/users/aviva"
  2638. }
  2639. </xmp>
  2640. </div>
  2641. #### Related to repositories
  2642. <pre class=simpledef>
  2643. Name: <dfn dfn export>Push</dfn>
  2644. URI: https://forgefed.org/ns#Push
  2645. Extends: [=Activity=]
  2646. Description:
  2647. Indicates that new content has been pushed to the [=Repository=].
  2648. </pre>
  2649. <div class=example>
  2650. <xmp highlight=json-ld line-highlight=7>
  2651. {
  2652. "@context": [
  2653. "https://www.w3.org/ns/activitystreams",
  2654. "https://forgefed.org/ns"
  2655. ],
  2656. "id": "https://example.dev/aviva/myproject/outbox/reBGo",
  2657. "type": "Push",
  2658. "actor": "https://example.dev/aviva/myproject",
  2659. "attributedTo": "https://example.dev/aviva",
  2660. "to": [
  2661. "https://example.dev/aviva",
  2662. "https://example.dev/aviva/followers",
  2663. "https://example.dev/aviva/myproject/followers"
  2664. ],
  2665. "summary": "<p>Aviva pushed a commit to myproject</p>",
  2666. "object": {
  2667. "type": "OrderedCollection",
  2668. "totalItems": 1,
  2669. "items": [
  2670. {
  2671. "id": "https://example.dev/aviva/myproject/commits/d96596230322716bd6f87a232a648ca9822a1c20",
  2672. "type": "Commit",
  2673. "attributedTo": "https://example.dev/aviva",
  2674. "context": "https://example.dev/aviva/myproject",
  2675. "hash": "d96596230322716bd6f87a232a648ca9822a1c20",
  2676. "created": "2019-11-03T13:43:59Z",
  2677. "summary": "Provide hints in sign-up form fields",
  2678. }
  2679. ]
  2680. },
  2681. "target": "https://example.dev/aviva/myproject/branches/master"
  2682. }
  2683. </xmp>
  2684. </div>
  2685. ### Actors
  2686. #### Software development components
  2687. <pre class=simpledef>
  2688. Name: <dfn dfn export>Repository</dfn>
  2689. URI: https://forgefed.org/ns#Repository
  2690. Extends: [=Object=]
  2691. Description:
  2692. Represents a version control system repository.
  2693. </pre>
  2694. <div class=example>
  2695. <xmp highlight=json-ld line-highlight=8>
  2696. {
  2697. "@context": [
  2698. "https://www.w3.org/ns/activitystreams",
  2699. "https://w3id.org/security/v1",
  2700. "https://forgefed.org/ns"
  2701. ],
  2702. "id": "https://dev.example/aviva/treesim",
  2703. "type": "Repository",
  2704. "publicKey": {
  2705. "id": "https://dev.example/aviva/treesim#main-key",
  2706. "owner": "https://dev.example/aviva/treesim",
  2707. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  2708. },
  2709. "inbox": "https://dev.example/aviva/treesim/inbox",
  2710. "outbox": "https://dev.example/aviva/treesim/outbox",
  2711. "followers": "https://dev.example/aviva/treesim/followers",
  2712. "team": "https://dev.example/aviva/treesim/team",
  2713. "name": "Tree Growth 3D Simulation",
  2714. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>"
  2715. }
  2716. </xmp>
  2717. </div>
  2718. <pre class=simpledef>
  2719. Name: <dfn dfn export>TicketTracker</dfn>
  2720. URI: https://forgefed.org/ns#TicketTracker
  2721. Extends: [=Object=]
  2722. Description:
  2723. Represents a [[wikipedia-ticket-tracker|ticket tracker]], i.e. a project
  2724. managing a list of work items.
  2725. </pre>
  2726. <div class=example>
  2727. <xmp highlight=json-ld line-highlight=8>
  2728. {
  2729. "@context": [
  2730. "https://www.w3.org/ns/activitystreams",
  2731. "https://w3id.org/security/v2",
  2732. "https://forgefed.org/ns"
  2733. ],
  2734. "id": "https://dev.example/aviva/treesim",
  2735. "type": ["Repository", "TicketTracker"],
  2736. "publicKey": {
  2737. "id": "https://dev.example/aviva/treesim#main-key",
  2738. "owner": "https://dev.example/aviva/treesim",
  2739. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  2740. },
  2741. "inbox": "https://dev.example/aviva/treesim/inbox",
  2742. "outbox": "https://dev.example/aviva/treesim/outbox",
  2743. "followers": "https://dev.example/aviva/treesim/followers",
  2744. "name": "Tree Growth 3D Simulation",
  2745. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>"
  2746. }
  2747. </xmp>
  2748. </div>
  2749. <pre class=simpledef>
  2750. Name: <dfn dfn export>PatchTracker</dfn>
  2751. URI: https://forgefed.org/ns#PatchTracker
  2752. Extends: [=Object=]
  2753. Description:
  2754. Represents a tracker of [[wikipedia-pr|merge requests]], i.e. a project
  2755. managing a list of patches or branches submitted as proposed changes to a
  2756. given [=Repository=].
  2757. </pre>
  2758. <div class=example>
  2759. <xmp highlight=json-ld line-highlight=8>
  2760. {
  2761. "@context": [
  2762. "https://www.w3.org/ns/activitystreams",
  2763. "https://w3id.org/security/v2",
  2764. "https://forgefed.org/ns"
  2765. ],
  2766. "id": "https://dev.example/aviva/treesim",
  2767. "type": ["Repository", "TicketTracker", "PatchTracker"],
  2768. "publicKey": {
  2769. "id": "https://dev.example/aviva/treesim#main-key",
  2770. "owner": "https://dev.example/aviva/treesim",
  2771. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  2772. },
  2773. "inbox": "https://dev.example/aviva/treesim/inbox",
  2774. "outbox": "https://dev.example/aviva/treesim/outbox",
  2775. "followers": "https://dev.example/aviva/treesim/followers",
  2776. "name": "Tree Growth 3D Simulation",
  2777. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>"
  2778. }
  2779. </xmp>
  2780. </div>
  2781. #### Organizational structure tools
  2782. <pre class=simpledef>
  2783. Name: <dfn dfn export>Project</dfn>
  2784. URI: https://forgefed.org/ns#Project
  2785. Extends: [=Object=]
  2786. Description:
  2787. Represents a project, a planned endeavor that involves usage of tools
  2788. related to the software development lifecycle. It may be a software
  2789. project, but may also be totally unrelated to software development. For
  2790. example, it may be a book that is being written using Markdown files kept
  2791. in a Git repository. A [=Project=] object is a way to collect forge related
  2792. components together under one title.
  2793. </pre>
  2794. <div class=example>
  2795. <xmp highlight=json-ld line-highlight=8>
  2796. {
  2797. "@context": [
  2798. "https://www.w3.org/ns/activitystreams",
  2799. "https://w3id.org/security/v2",
  2800. "https://forgefed.org/ns"
  2801. ],
  2802. "id": "https://dev.example/projects/wanderer",
  2803. "type": "Project",
  2804. "name": "Wanderer",
  2805. "summary": "3D nature exploration game",
  2806. "components": {
  2807. "type": "Collection",
  2808. "totalItems": 7,
  2809. "items": [
  2810. "https://dev.example/repos/opengl-vegetation",
  2811. "https://dev.example/repos/opengl-vegetation/patch-tracker",
  2812. "https://dev.example/repos/treesim",
  2813. "https://dev.example/repos/treesim/patch-tracker",
  2814. "https://dev.example/repos/wanderer",
  2815. "https://dev.example/repos/wanderer/patch-tracker",
  2816. "https://dev.example/issue-trackers/wanderer"
  2817. ]
  2818. },
  2819. "publicKey": {
  2820. "id": "https://dev.example/projects/wanderer#main-key",
  2821. "owner": "https://dev.example/projects/wanderer",
  2822. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  2823. },
  2824. "inbox": "https://dev.example/projects/wanderer/inbox",
  2825. "outbox": "https://dev.example/projects/wanderer/outbox",
  2826. "followers": "https://dev.example/projects/wanderer/followers"
  2827. }
  2828. </xmp>
  2829. </div>
  2830. <pre class=simpledef>
  2831. Name: <dfn dfn export>Team</dfn>
  2832. URI: https://forgefed.org/ns#Team
  2833. Extends: [=Object=]
  2834. Description:
  2835. Represents a group of people working together, collaborating on shared
  2836. resources. Each member [=Person=] in the team has a defined role, affecting
  2837. the level of access they have to the team's shared resources and to
  2838. managing the team itself.
  2839. </pre>
  2840. <div class=example>
  2841. <xmp highlight=json-ld line-highlight=8>
  2842. {
  2843. "@context": [
  2844. "https://www.w3.org/ns/activitystreams",
  2845. "https://w3id.org/security/v2",
  2846. "https://forgefed.org/ns"
  2847. ],
  2848. "id": "https://dev.example/teams/mobilizon-dev-team",
  2849. "type": "Team",
  2850. "name": "Mobilizon Development Team",
  2851. "summary": "We're creating a federated tool for organizing events!",
  2852. "members": {
  2853. "type": "Collection",
  2854. "totalItems": 3,
  2855. "items": [
  2856. { "type": "Relationship",
  2857. "subject": "https://dev.example/teams/mobilizon-dev-team",
  2858. "relationship": "hasMember",
  2859. "object": "https://dev.example/people/alice",
  2860. "tag": "https://roles.example/admin"
  2861. },
  2862. { "type": "Relationship",
  2863. "subject": "https://dev.example/teams/mobilizon-dev-team",
  2864. "relationship": "hasMember",
  2865. "object": "https://dev.example/people/bob",
  2866. "tag": "maintain"
  2867. },
  2868. { "type": "Relationship",
  2869. "subject": "https://dev.example/teams/mobilizon-dev-team",
  2870. "relationship": "hasMember",
  2871. "object": "https://dev.example/people/celine",
  2872. "tag": "develop"
  2873. }
  2874. ]
  2875. },
  2876. "subteams": {
  2877. "type": "Collection",
  2878. "totalItems": 2,
  2879. "items": [
  2880. "https://dev.example/teams/mobilizon-backend-team",
  2881. "https://dev.example/teams/mobilizon-frontend-team"
  2882. ]
  2883. },
  2884. "context": "https://dev.example/teams/framasoft-developers",
  2885. "publicKey": {
  2886. "id": "https://dev.example/teams/mobilizon-dev-team#main-key",
  2887. "owner": "https://dev.example/teams/mobilizon-dev-team",
  2888. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  2889. },
  2890. "inbox": "https://dev.example/teams/mobilizon-dev-team/inbox",
  2891. "outbox": "https://dev.example/teams/mobilizon-dev-team/outbox",
  2892. "followers": "https://dev.example/teams/mobilizon-dev-team/followers"
  2893. }
  2894. </xmp>
  2895. </div>
  2896. ### Objects
  2897. <pre class=simpledef>
  2898. Name: <dfn dfn export>CapabilityUsage</dfn>
  2899. URI: https://forgefed.org/ns#CapabilityUsage
  2900. Extends: [=Object=]
  2901. Values:
  2902. This specification defines 3&#x3a; [=gatherAndConvey=], [=distribute=] and
  2903. [=invoke]
  2904. Description:
  2905. Represents a mode of using a [=Grant=] as an Object Capability (OCAP).
  2906. There are two conceptual operations for `Grant`s&#x3a; Invocation (acting
  2907. on the resource under the specified role) and Delegation (passing on the
  2908. access to more actors, possibly with reduced privileges). A value of this
  2909. type refers to one or both of these operations, and possibly to more
  2910. specific conditions and restrictions on applying them.
  2911. </pre>
  2912. <div class=example>
  2913. <xmp highlight=json-ld line-highlight=7>
  2914. TODO
  2915. </xmp>
  2916. </div>
  2917. <pre class=simpledef>
  2918. Name: <dfn dfn export>Role</dfn>
  2919. URI: https://forgefed.org/ns#Role
  2920. Extends: [=Object=]
  2921. Values:
  2922. [=visit=], [=report=], [=triage=], [=write=], [=maintain=], [=admin=],
  2923. [=delegate=]
  2924. Description:
  2925. Represents a role that an actor has within a [=Team=], or a role defining
  2926. the level of access an actor has to a resource.
  2927. </pre>
  2928. <div class=example>
  2929. <xmp highlight=json-ld line-highlight=7>
  2930. TODO
  2931. </xmp>
  2932. </div>
  2933. <pre class=simpledef>
  2934. Name: <dfn dfn export>Branch</dfn>
  2935. URI: https://forgefed.org/ns#Branch
  2936. Extends: [=Object=]
  2937. Description:
  2938. Represents a named variable reference to a version of the [=Repository=],
  2939. typically used for committing changes in parallel to other development, and
  2940. usually eventually merging the changes into the main history line.
  2941. </pre>
  2942. <div class=example>
  2943. <xmp highlight=json-ld line-highlight=7>
  2944. {
  2945. "@context": [
  2946. "https://www.w3.org/ns/activitystreams",
  2947. "https://forgefed.org/ns"
  2948. ],
  2949. "id": "https://example.dev/luke/myrepo/branches/master",
  2950. "type": "Branch",
  2951. "name": "master",
  2952. "context": "https://example.dev/luke/myrepo",
  2953. "ref": "refs/heads/master"
  2954. }
  2955. </xmp>
  2956. </div>
  2957. <pre class=simpledef>
  2958. Name: <dfn dfn export>Commit</dfn>
  2959. URI: https://forgefed.org/ns#Commit
  2960. Extends: [=Object=]
  2961. Description:
  2962. Represents a named set of changes in the history of a [=Repository=]. This
  2963. is called "commit" in Git, Mercurial and Monotone; "patch" in Darcs;
  2964. sometimes called "change set". Note that `Commit` is a set of changes that
  2965. already exists in a repo's history, while a [=Patch=] is a separate
  2966. proposed change set, that *could* be applied and pushed to a repo,
  2967. resulting with a `Commit`.
  2968. </pre>
  2969. <div class=example>
  2970. <xmp highlight=json-ld line-highlight=7>
  2971. {
  2972. "@context": [
  2973. "https://www.w3.org/ns/activitystreams",
  2974. "https://forgefed.org/ns"
  2975. ],
  2976. "id": "https://example.dev/alice/myrepo/commits/109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
  2977. "type": "Commit",
  2978. "context": "https://example.dev/alice/myrepo",
  2979. "attributedTo": "https://example.dev/bob",
  2980. "committedBy": "https://example.dev/alice",
  2981. "hash": "109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
  2982. "summary": "Add an installation script, fixes issue #89",
  2983. "description": {
  2984. "mediaType": "text/plain",
  2985. "content": "It's about time people can install on their computers!"
  2986. },
  2987. "created": "2019-07-11T12:34:56Z",
  2988. "committed": "2019-07-26T23:45:01Z"
  2989. }
  2990. </xmp>
  2991. </div>
  2992. <pre class=simpledef>
  2993. Name: <dfn dfn export>Patch</dfn>
  2994. URI: https://forgefed.org/ns#Patch
  2995. Extends: [=Object=]
  2996. Description:
  2997. Represents a named set of changes that are being proposed for applying to a
  2998. specific [=Repository=].
  2999. </pre>
  3000. <div class=example>
  3001. <xmp highlight=json-ld line-highlight=7>
  3002. {
  3003. "@context": [
  3004. "https://www.w3.org/ns/activitystreams",
  3005. "https://forgefed.org/ns"
  3006. ],
  3007. "id": "https://dev.example/aviva/game-of-life/pulls/825/versions/1/patches/1",
  3008. "type": "Patch",
  3009. "attributedTo": "https://forge.example/luke",
  3010. "context": "https://dev.example/aviva/game-of-life/pulls/825/versions/1",
  3011. "mediaType": "application/x-git-patch",
  3012. "content": "From c9ae5f4ff4a330b6e1196ceb7db1665bd4c1..."
  3013. }
  3014. </xmp>
  3015. </div>
  3016. <pre class=simpledef>
  3017. Name: <dfn dfn export>TicketDependency</dfn>
  3018. URI: https://forgefed.org/ns#TicketDependency
  3019. Extends: [=Relationship=]
  3020. Description:
  3021. Represents a relationship between 2 [=Ticket=]s, in which the resolution of
  3022. one ticket requires the other ticket to be resolved too. It MUST specify
  3023. the [=subject=], [=object=] and [=relationship=] properties, and the
  3024. `relationship` property MUST be [=dependsOn=].
  3025. </pre>
  3026. <div class=example>
  3027. <xmp highlight=json-ld line-highlight=6>
  3028. {
  3029. "@context": [
  3030. "https://www.w3.org/ns/activitystreams",
  3031. "https://forgefed.org/ns"
  3032. ],
  3033. "type": ["Relationship", "TicketDependency"],
  3034. "id": "https://example.dev/ticket-deps/2342593",
  3035. "attributedTo": "https://example.dev/alice",
  3036. "summary": "Alice's ticket depends on Bob's ticket",
  3037. "published": "2019-07-11T12:34:56Z",
  3038. "subject": "https://example.dev/alice/myproj/issues/42",
  3039. "relationship": "dependsOn",
  3040. "object": "https://dev.community/bob/coolproj/issues/85"
  3041. }
  3042. </xmp>
  3043. </div>
  3044. <pre class=simpledef>
  3045. Name: <dfn dfn export>Ticket</dfn>
  3046. URI: https://forgefed.org/ns#Ticket
  3047. Extends: [=Object=]
  3048. Description:
  3049. Represents an item that requires work or attention. Tickets exist in the
  3050. context of a project (which may or may not be a version-control
  3051. repository), and are used to track ideas, proposals, tasks, bugs and more.
  3052. </pre>
  3053. <div class=example>
  3054. <xmp highlight=json-ld line-highlight=6>
  3055. {
  3056. "@context": [
  3057. "https://www.w3.org/ns/activitystreams",
  3058. "https://forgefed.org/ns"
  3059. ],
  3060. "type": "Ticket",
  3061. "id": "https://example.dev/alice/myrepo/issues/42",
  3062. "context": "https://example.dev/alice/myrepo",
  3063. "attributedTo": "https://dev.community/bob",
  3064. "summary": "Nothing works!",
  3065. "content": "<p>Please fix. <i>Everything</i> is broken!</p>",
  3066. "mediaType": "text/html",
  3067. "source": {
  3068. "content": "Please fix. *Everything* is broken!",
  3069. "mediaType": "text/markdown; variant=CommonMark"
  3070. },
  3071. "assignedTo": "https://example.dev/alice",
  3072. "isResolved": false
  3073. }
  3074. </xmp>
  3075. </div>
  3076. ## Properties
  3077. ### General-purpose
  3078. <pre class=simpledef>
  3079. Name: <dfn dfn export>earlyItems</dfn>
  3080. URI: https://forgefed.org/ns#earlyItems
  3081. Domain: [=OrderedCollection=]
  3082. Range: Ordered list of [ [=Object=] | [=Link=] ]
  3083. Functional: No
  3084. Inverse of: None
  3085. Description:
  3086. In an ordered collection (or an ordered collection page) in which [=items=]
  3087. (or [=orderedItems=]) contains a continuous subset of the collection's
  3088. items from one end, `earlyItems` identifiers a continuous subset from the
  3089. other end. For example, if `items` lists the chronologically latest items,
  3090. `earlyItems` would list the chrologically earliest items. The ordering rule
  3091. for items in `earlyItems` MUST be the same as in `items`. For examle, if
  3092. `items` lists items in reverse chronogical order, then so does
  3093. `earlyItems`.
  3094. </pre>
  3095. <div class=example>
  3096. <xmp highlight=json-ld line-highlight=14>
  3097. {
  3098. "@context": [
  3099. "https://www.w3.org/ns/activitystreams",
  3100. "https://forgefed.org/ns"
  3101. ],
  3102. "id": "https://dev.example/aviva/outbox",
  3103. "type": "OrderedCollection",
  3104. "totalItems": 712,
  3105. "orderedItems": [
  3106. "https://dev.example/aviva/outbox/712",
  3107. "https://dev.example/aviva/outbox/711",
  3108. "https://dev.example/aviva/outbox/710"
  3109. ],
  3110. "earlyItems": [
  3111. "https://dev.example/aviva/outbox/3",
  3112. "https://dev.example/aviva/outbox/2",
  3113. "https://dev.example/aviva/outbox/1"
  3114. ]
  3115. }
  3116. </xmp>
  3117. </div>
  3118. <pre class=simpledef>
  3119. Name: <dfn dfn export>previousVersions</dfn>
  3120. URI: https://forgefed.org/ns#previousVersions
  3121. Domain: [=Object=]
  3122. Range: `rdf:List` of objects of the same `@type` as the subject
  3123. Functional: Yes
  3124. Inverse of: None, but see [=currentVersion=]
  3125. Description:
  3126. Specifies the previous versions of the subject, as an ordered list in
  3127. reverse chronological order.
  3128. </pre>
  3129. <div class=example>
  3130. <xmp highlight=json-ld line-highlight=10>
  3131. {
  3132. "@context": [
  3133. "https://www.w3.org/ns/activitystreams",
  3134. "https://forgefed.org/ns"
  3135. ],
  3136. "id": "https://dev.example/aviva/notes/107",
  3137. "type": "Note",
  3138. "attributedTo": "https://dev.example/aviva",
  3139. "content": "I agree!!!!! (edit: fixed a typo)",
  3140. "previousVersions": [
  3141. "https://dev.example/aviva/notes/107_old_version",
  3142. "https://dev.example/aviva/notes/107_very_old_version",
  3143. "https://dev.example/aviva/notes/107_ancient_version"
  3144. ]
  3145. }
  3146. </xmp>
  3147. </div>
  3148. <pre class=simpledef>
  3149. Name: <dfn dfn export>currentVersion</dfn>
  3150. URI: https://forgefed.org/ns#currentVersion
  3151. Domain: [=Object=]
  3152. Range: [=Object=], of the same `@type` as the subject
  3153. Functional: Yes
  3154. Inverse of: None, but see [=previousVersions=]
  3155. Description:
  3156. Specifies the latest. current, up-to-date version of the subject. Once the
  3157. subject specifies the `currentVersion` property, it SHOULD NOT get any
  3158. changes to any other properties. The exception is `currentVersion` itself,
  3159. which MUST be updated whenever needed, to always point to the latest
  3160. version.
  3161. </pre>
  3162. <div class=example>
  3163. <xmp highlight=json-ld line-highlight=10>
  3164. {
  3165. "@context": [
  3166. "https://www.w3.org/ns/activitystreams",
  3167. "https://forgefed.org/ns"
  3168. ],
  3169. "id": "https://dev.example/aviva/notes/107_old_version",
  3170. "type": "Note",
  3171. "attributedTo": "https://dev.example/aviva",
  3172. "content": "I agree!!111",
  3173. "currentVersion": "https://dev.example/aviva/notes/107"
  3174. }
  3175. </xmp>
  3176. </div>
  3177. ### Projects
  3178. <pre class=simpledef>
  3179. Name: <dfn dfn export>components</dfn>
  3180. URI: https://forgefed.org/ns#components
  3181. Domain: [=Project=]
  3182. Range: [=Collection=]
  3183. Functional: Yes
  3184. Inverse of:
  3185. None, but see the usage of [=context=] in the Modeling specification, e.g.
  3186. in [[#Repository]]
  3187. Description:
  3188. Identifies a [=Collection=] listing actors whose services and resources are
  3189. considered to be components of this project.
  3190. </pre>
  3191. <div class=example>
  3192. <xmp highlight=json-ld line-highlight=11>
  3193. {
  3194. "@context": [
  3195. "https://www.w3.org/ns/activitystreams",
  3196. "https://forgefed.org/ns"
  3197. ],
  3198. "id": "https://dev.example/projects/wanderer",
  3199. "type": "Project",
  3200. "name": "Wanderer",
  3201. "summary": "3D nature exploration game",
  3202. "components": {
  3203. "type": "Collection",
  3204. "totalItems": 7,
  3205. "items": [
  3206. "https://dev.example/repos/opengl-vegetation",
  3207. "https://dev.example/repos/opengl-vegetation/patch-tracker",
  3208. "https://dev.example/repos/treesim",
  3209. "https://dev.example/repos/treesim/patch-tracker",
  3210. "https://dev.example/repos/wanderer",
  3211. "https://dev.example/repos/wanderer/patch-tracker",
  3212. "https://dev.example/issue-trackers/wanderer"
  3213. ]
  3214. },
  3215. "inbox": "https://dev.example/projects/wanderer/inbox",
  3216. "outbox": "https://dev.example/projects/wanderer/outbox",
  3217. "followers": "https://dev.example/projects/wanderer/followers"
  3218. }
  3219. </xmp>
  3220. </div>
  3221. <pre class=simpledef>
  3222. Name: <dfn dfn export>subprojects</dfn>
  3223. URI: https://forgefed.org/ns#subprojects
  3224. Domain: [=Project=]
  3225. Range: [=Collection=] of [=Project=]s
  3226. Functional: Yes
  3227. Inverse of:
  3228. None, but see the usage of [=context=] in [[#Project]]
  3229. Description:
  3230. Identifies a [=Collection=] listing the subprojects of this [=Project=].
  3231. </pre>
  3232. <div class=example>
  3233. <xmp highlight=json-ld line-highlight=11>
  3234. {
  3235. "@context": [
  3236. "https://www.w3.org/ns/activitystreams",
  3237. "https://forgefed.org/ns"
  3238. ],
  3239. "id": "https://dev.example/projects/wanderer",
  3240. "type": "Project",
  3241. "name": "Wanderer",
  3242. "summary": "3D nature exploration game",
  3243. "subprojects": {
  3244. "type": "Collection",
  3245. "totalItems": 2,
  3246. "items": [
  3247. "https://dev.example/projects/nature-3d-models",
  3248. "https://dev.example/projects/wanderer-fundraising"
  3249. ]
  3250. },
  3251. "inbox": "https://dev.example/projects/wanderer/inbox",
  3252. "outbox": "https://dev.example/projects/wanderer/outbox",
  3253. "followers": "https://dev.example/projects/wanderer/followers"
  3254. }
  3255. </xmp>
  3256. </div>
  3257. ### Team membership and nesting
  3258. <pre class=simpledef>
  3259. Name: <dfn dfn export>hasMember</dfn>
  3260. URI: https://forgefed.org/ns#hasMember
  3261. Domain: [=Team=]
  3262. Range: [=Person=]
  3263. Functional: No
  3264. Inverse of: None
  3265. Description:
  3266. Identifier a [=Person=] who is a member of this [=Team=].
  3267. </pre>
  3268. <div class=example>
  3269. <xmp highlight=json-ld line-highlight=9>
  3270. TODO
  3271. </xmp>
  3272. </div>
  3273. <pre class=simpledef>
  3274. Name: <dfn dfn export>members</dfn>
  3275. URI: https://forgefed.org/ns#members
  3276. Domain: [=Team=]
  3277. Range:
  3278. [=Collection=] of [=Relationship=]s whose [=relationship=] is [=hasMember=]
  3279. and whose [=subject=] is this `Team`.
  3280. Functional: Yes
  3281. Inverse of: None
  3282. Description:
  3283. Identifies a collection of the members of this [=Team=], represented as
  3284. [=hasMember=] [=Relationship=]s.
  3285. </pre>
  3286. <div class=example>
  3287. <xmp highlight=json-ld line-highlight=9>
  3288. TODO
  3289. </xmp>
  3290. </div>
  3291. <pre class=simpledef>
  3292. Name: <dfn dfn export>subteams</dfn>
  3293. URI: https://forgefed.org/ns#subteams
  3294. Domain: [=Team=]
  3295. Range: [=Collection=] of [=Team=]s.
  3296. Functional: Yes
  3297. Inverse of: None
  3298. Description:
  3299. Identifies a collection of the subteams of this [=Team=], i.e. teams whose
  3300. members inherit the access that this team's members have to projects and to
  3301. project components (such as [=Repository=]s).
  3302. </pre>
  3303. <div class=example>
  3304. <xmp highlight=json-ld line-highlight=9>
  3305. TODO
  3306. </xmp>
  3307. </div>
  3308. ### Ticket assignment and resolution
  3309. <pre class=simpledef>
  3310. Name: <dfn dfn export>assignedTo</dfn>
  3311. URI: https://forgefed.org/ns#assignedTo
  3312. Domain: [=Ticket=]
  3313. Range: [=Person=]
  3314. Functional: Yes
  3315. Inverse of: None
  3316. Description:
  3317. Identifies the [=Person=] assigned to work on this [=Ticket=].
  3318. </pre>
  3319. <div class=example>
  3320. <xmp highlight=json-ld line-highlight=9>
  3321. TODO
  3322. </xmp>
  3323. </div>
  3324. <pre class=simpledef>
  3325. Name: <dfn dfn export>isResolved</dfn>
  3326. URI: https://forgefed.org/ns#isResolved
  3327. Domain: [=Ticket=]
  3328. Range: `xsd:boolean`
  3329. Functional: Yes
  3330. Inverse of: None
  3331. Description:
  3332. Specifies whether the [=Ticket=] is closed, i.e. the work on it is done and
  3333. it doesn't need to attract attention anymore.
  3334. </pre>
  3335. <div class=example>
  3336. <xmp highlight=json-ld line-highlight=9>
  3337. TODO
  3338. </xmp>
  3339. </div>
  3340. <pre class=simpledef>
  3341. Name: <dfn dfn export>resolvedBy</dfn>
  3342. URI: https://forgefed.org/ns#resolvedBy
  3343. Domain: [=Ticket=]
  3344. Range: [=Object=] than is an actor, or [=Activity=]
  3345. Functional: Yes
  3346. Inverse of: None
  3347. Description:
  3348. Identifies the Actor who has resolved the [=Ticket=], or the activity that
  3349. has resolved the Ticket.
  3350. </pre>
  3351. <div class=example>
  3352. <xmp highlight=json-ld line-highlight=9>
  3353. TODO
  3354. </xmp>
  3355. </div>
  3356. <pre class=simpledef>
  3357. Name: <dfn dfn export>resolved</dfn>
  3358. URI: https://forgefed.org/ns#resolved
  3359. Domain: [=Ticket=]
  3360. Range: `xsd:dateTime`
  3361. Functional: Yes
  3362. Inverse of: None
  3363. Description:
  3364. For a resolved [=Ticket=], specifies the time the Ticket has been resolved.
  3365. </pre>
  3366. <div class=example>
  3367. <xmp highlight=json-ld line-highlight=9>
  3368. TODO
  3369. </xmp>
  3370. </div>
  3371. ### Ticket dependencies
  3372. <pre class=simpledef>
  3373. Name: <dfn dfn export>dependsOn</dfn>
  3374. URI: https://forgefed.org/ns#dependsOn
  3375. Domain: [=Ticket=]
  3376. Range: [=Ticket=]
  3377. Functional: No
  3378. Inverse of: [=dependedBy=]
  3379. Description:
  3380. Identifies one or more tickets on which this [=Ticket=] depends, i.e. it
  3381. can't be resolved without those tickets being resolved too.
  3382. </pre>
  3383. <div class=example>
  3384. <xmp highlight=json-ld line-highlight=9>
  3385. TODO
  3386. </xmp>
  3387. </div>
  3388. <pre class=simpledef>
  3389. Name: <dfn dfn export>dependedBy</dfn>
  3390. URI: https://forgefed.org/ns#dependedBy
  3391. Domain: [=Ticket=]
  3392. Range: [=Ticket=]
  3393. Functional: No
  3394. Inverse of: [=dependsOn=]
  3395. Description:
  3396. Identifies one or more tickets which depend on this [=Ticket=], i.e. they
  3397. can't be resolved without this tickets being resolved too.
  3398. </pre>
  3399. <div class=example>
  3400. <xmp highlight=json-ld line-highlight=9>
  3401. TODO
  3402. </xmp>
  3403. </div>
  3404. <pre class=simpledef>
  3405. Name: <dfn dfn export>dependencies</dfn>
  3406. URI: https://forgefed.org/ns#dependencies
  3407. Domain: [=Ticket=]
  3408. Range: [=Collection=] of items of type [=TicketDependency=]
  3409. Functional: Yes
  3410. Inverse of: None
  3411. Description:
  3412. Identifies a [=Collection=] of [=TicketDependency=] which specify tickets
  3413. that this [=Ticket=] depends on, i.e. this ticket is the [=subject=] of the
  3414. [=dependsOn=] relationship.
  3415. </pre>
  3416. <div class=example>
  3417. <xmp highlight=json-ld line-highlight=9>
  3418. TODO
  3419. </xmp>
  3420. </div>
  3421. <pre class=simpledef>
  3422. Name: <dfn dfn export>dependants</dfn>
  3423. URI: https://forgefed.org/ns#dependants
  3424. Domain: [=Ticket=]
  3425. Range: [=Collection=] of items of type [=TicketDependency=]
  3426. Functional: Yes
  3427. Inverse of: None
  3428. Description:
  3429. Identifies a [=Collection=] of [=TicketDependency=] which specify tickets
  3430. that depends on this [=Ticket=], i.e. this ticket is the [=object=] of the
  3431. [=dependsOn=] relationship. Often called "reverse dependencies".
  3432. </pre>
  3433. <div class=example>
  3434. <xmp highlight=json-ld line-highlight=9>
  3435. TODO
  3436. </xmp>
  3437. </div>
  3438. ### Repository cloning
  3439. <pre class=simpledef>
  3440. Name: <dfn dfn export>cloneUri</dfn>
  3441. URI: https://forgefed.org/ns#cloneUri
  3442. Domain: [=Repository=]
  3443. Range: [=Object=]
  3444. Functional: No
  3445. Inverse of: None
  3446. Description:
  3447. An endpoint that can be used with a VCS protocol such as Git or Mercurial
  3448. to access a given repository.
  3449. </pre>
  3450. <div class=example>
  3451. <xmp highlight=json-ld line-highlight=9>
  3452. TODO
  3453. </xmp>
  3454. </div>
  3455. ### Repository pushing
  3456. <pre class=simpledef>
  3457. Name: <dfn dfn export>hashBefore</dfn>
  3458. URI: https://forgefed.org/ns#hashBefore
  3459. Domain: [=Push=]
  3460. Range: `xsd:string` of hexadecimal digit ASCII characters
  3461. Functional: Yes
  3462. Inverse of: None
  3463. Description:
  3464. Specifies the hash of the commit that the pushed branch/repo was pointing
  3465. to *right before* the push happpened.
  3466. </pre>
  3467. <div class=example>
  3468. <xmp highlight=json-ld line-highlight=9>
  3469. TODO
  3470. </xmp>
  3471. </div>
  3472. <pre class=simpledef>
  3473. Name: <dfn dfn export>hashAfter</dfn>
  3474. URI: https://forgefed.org/ns#hashAfter
  3475. Domain: [=Push=]
  3476. Range: `xsd:string` of hexadecimal digit ASCII characters
  3477. Functional: Yes
  3478. Inverse of: None
  3479. Description:
  3480. Specifies the hash of the commit that the pushed branch/repo was pointing
  3481. to *right after* the push happpened.
  3482. </pre>
  3483. <div class=example>
  3484. <xmp highlight=json-ld line-highlight=9>
  3485. TODO
  3486. </xmp>
  3487. </div>
  3488. ### Commits and branches
  3489. <pre class=simpledef>
  3490. Name: <dfn dfn export lt="prop-repository">repository</dfn> (DEPRECATED)
  3491. URI: https://forgefed.org/ns#repository
  3492. Domain: [=Commit=]
  3493. Range: [=Repository=]
  3494. Functional: Yes
  3495. Inverse of: None
  3496. Description:
  3497. Identifies the repository to which a commit belongs. DEPRECATED: Use the
  3498. standard ActivityPub `context` property instead.
  3499. </pre>
  3500. <div class=example>
  3501. <xmp highlight=json-ld line-highlight=9>
  3502. TODO
  3503. </xmp>
  3504. </div>
  3505. <pre class=simpledef>
  3506. Name: <dfn dfn export>description</dfn>
  3507. URI: https://forgefed.org/ns#description
  3508. Domain: [=Commit=]
  3509. Range:
  3510. [=Object=], specifying [=content=] and [=mediaType=]. The `mediaType`
  3511. SHOULD be `"text/plain"`.
  3512. Functional: Yes
  3513. Inverse of: None
  3514. Description:
  3515. Specifies the description text of a [=Commit=], which is an optional
  3516. possibly multi-line text provided in addition to the one-line commit title.
  3517. The range of the `description` property works the same way the range of the
  3518. ActivityPub [=source=] property works.
  3519. </pre>
  3520. <div class=example>
  3521. <xmp highlight=json-ld line-highlight=14>
  3522. {
  3523. "@context": [
  3524. "https://www.w3.org/ns/activitystreams",
  3525. "https://forgefed.org/ns"
  3526. ],
  3527. "id": "https://example.dev/alice/myrepo/commits/109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
  3528. "type": "Commit",
  3529. "context": "https://example.dev/alice/myrepo",
  3530. "attributedTo": "https://example.dev/bob",
  3531. "hash": "109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
  3532. "created": "2019-07-11T12:34:56Z",
  3533. "summary": "Add an installation script, fixes issue #89",
  3534. "description": {
  3535. "mediaType": "text/plain",
  3536. "content": "It's about time people can install on their computers!"
  3537. },
  3538. }
  3539. </xmp>
  3540. </div>
  3541. <pre class=simpledef>
  3542. Name: <dfn dfn export>committedBy</dfn>
  3543. URI: https://forgefed.org/ns#committedBy
  3544. Domain: [=Commit=]
  3545. Range: [=Object=]
  3546. Functional: Yes
  3547. Inverse of: None
  3548. Description:
  3549. Identifies the actor (usually a person, but could be something else, e.g. a
  3550. bot) that added a set of changes to the version-control [=Repository=].
  3551. Sometimes the author of the changes and the committer of those changes
  3552. aren't the same actor, in which case the `committedBy` property can be used
  3553. to specify who added the changes to the repository. For example, when
  3554. applying a patch to a repository, e.g. a Git repository, the author would
  3555. be the person who made the patch, and the committer would be the person who
  3556. applied the patch to their copy of the repository.
  3557. </pre>
  3558. <div class=example>
  3559. <xmp highlight=json-ld line-highlight=9>
  3560. TODO
  3561. </xmp>
  3562. </div>
  3563. <pre class=simpledef>
  3564. Name: <dfn dfn export>hash</dfn>
  3565. URI: https://forgefed.org/ns#hash
  3566. Domain: [=Commit=]
  3567. Range: `xsd:string` of hexadecimal digit ASCII characters
  3568. Functional: Yes
  3569. Inverse of: None
  3570. Description:
  3571. Specifies the hash associated with a [=Commit=], which is a unique
  3572. identifier of the commit within the [=Repository=], usually generated as a
  3573. cryptographic hash function of some (or all) of the commit's data or
  3574. metadata. For example, in Git it would be the SHA1 hash of the commit; in
  3575. Darcs it would be the SHA1 hash of the patch info.
  3576. </pre>
  3577. <div class=example>
  3578. <xmp highlight=json-ld line-highlight=9>
  3579. TODO
  3580. </xmp>
  3581. </div>
  3582. <pre class=simpledef>
  3583. Name: <dfn dfn export>committed</dfn>
  3584. URI: https://forgefed.org/ns#committed
  3585. Domain: [=Commit=]
  3586. Range: `xsd:dateTime`
  3587. Functional: Yes
  3588. Inverse of: None
  3589. Description:
  3590. Specifies the time that a set of changes was committed into the
  3591. [=Repository=] and became a [=Commit=] in it. This can be different from
  3592. the time the set of changes was produced, e.g. if one person creates a
  3593. patch and sends to another, and the other person then applies the patch to
  3594. their copy of the repository. We call the former event "created" and the
  3595. latter event "committed", and this latter event is specified by the
  3596. `committed` property.
  3597. </pre>
  3598. <div class=example>
  3599. <xmp highlight=json-ld line-highlight=9>
  3600. TODO
  3601. </xmp>
  3602. </div>
  3603. <pre class=simpledef>
  3604. Name: <dfn dfn export>filesAdded</dfn>
  3605. URI: https://forgefed.org/ns#filesAdded
  3606. Domain: [=Commit=]
  3607. Range: `xsd:string`
  3608. Functional: No
  3609. Inverse of: None
  3610. Description:
  3611. Specifies a filename, as a relative path, relative to the top of the tree
  3612. of files in the [=Repository=], of a file that got added in this
  3613. [=Commit=], and didn't exist in the previous version of the tree.
  3614. </pre>
  3615. <div class=example>
  3616. <xmp highlight=json-ld line-highlight=9>
  3617. TODO
  3618. </xmp>
  3619. </div>
  3620. <pre class=simpledef>
  3621. Name: <dfn dfn export>filesModified</dfn>
  3622. URI: https://forgefed.org/ns#filesModified
  3623. Domain: [=Commit=]
  3624. Range: `xsd:string`
  3625. Functional: No
  3626. Inverse of: None
  3627. Description:
  3628. Specifies a filename, as a relative path, relative to the top of the tree
  3629. of files in the [=Repository=], of a file that existed in the previous
  3630. version of the tree, and its contents got modified in this [=Commit=].
  3631. </pre>
  3632. <div class=example>
  3633. <xmp highlight=json-ld line-highlight=9>
  3634. TODO
  3635. </xmp>
  3636. </div>
  3637. <pre class=simpledef>
  3638. Name: <dfn dfn export>filesRemoved</dfn>
  3639. URI: https://forgefed.org/ns#filesRemoved
  3640. Domain: [=Commit=]
  3641. Range: `xsd:string`
  3642. Functional: No
  3643. Inverse of: None
  3644. Description:
  3645. Specifies a filename, as a relative path, relative to the top of the tree
  3646. of files in the [=Repository=], of a file that existed in the previous
  3647. version of the tree, and got removed from the tree in this [=Commit=].
  3648. </pre>
  3649. <div class=example>
  3650. <xmp highlight=json-ld line-highlight=9>
  3651. TODO
  3652. </xmp>
  3653. </div>
  3654. <pre class=simpledef>
  3655. Name: <dfn dfn export>ref</dfn>
  3656. URI: https://forgefed.org/ns#ref
  3657. Domain: [=Branch=]
  3658. Range: `xsd:string`
  3659. Functional: Yes
  3660. Inverse of: None
  3661. Description:
  3662. Specifies an identifier for a [=Branch=], that is used in the
  3663. [=Repository=] to uniquely refer to it. For example, in Git,
  3664. "refs/heads/master" would be the `ref` of the master branch.
  3665. </pre>
  3666. <div class=example>
  3667. <xmp highlight=json-ld line-highlight=11>
  3668. {
  3669. "@context": [
  3670. "https://www.w3.org/ns/activitystreams",
  3671. "https://forgefed.org/ns"
  3672. ],
  3673. "id": "https://example.dev/luke/myrepo/branches/master",
  3674. "type": "Branch",
  3675. "name": "master",
  3676. "context": "https://example.dev/luke/myrepo",
  3677. "ref": "refs/heads/master"
  3678. }
  3679. </xmp>
  3680. </div>
  3681. ### Activity addressing
  3682. <pre class=simpledef>
  3683. Name: <dfn dfn export lt="prop-team">team</dfn>
  3684. URI: https://forgefed.org/ns#team
  3685. Domain: [=Object=]
  3686. Range: [=Collection=] of actors
  3687. Functional: Yes
  3688. Inverse of: None
  3689. Description:
  3690. Specifies a [=Collection=] of actors who are working on the object, or
  3691. responsible for it, or managing or administrating it, or having edit access
  3692. to it. For example, for a [=Repository=], it could be the people who have
  3693. push/edit access, the "collaborators" of the repository.
  3694. </pre>
  3695. <div class=example>
  3696. A repository *https://dev.example/aviva/treesim*:
  3697. <xmp highlight=json-ld line-highlight=20>
  3698. {
  3699. "@context": [
  3700. "https://www.w3.org/ns/activitystreams",
  3701. "https://w3id.org/security/v1",
  3702. "https://forgefed.org/ns"
  3703. ],
  3704. "id": "https://dev.example/aviva/treesim",
  3705. "type": "Repository",
  3706. "publicKey": {
  3707. "id": "https://dev.example/aviva/treesim#main-key",
  3708. "owner": "https://dev.example/aviva/treesim",
  3709. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  3710. },
  3711. "inbox": "https://dev.example/aviva/treesim/inbox",
  3712. "outbox": "https://dev.example/aviva/treesim/outbox",
  3713. "followers": "https://dev.example/aviva/treesim/followers",
  3714. "name": "Tree Growth 3D Simulation",
  3715. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>",
  3716. "team": "https://dev.example/aviva/treesim/team"
  3717. }
  3718. </xmp>
  3719. The repository's team *https://dev.example/aviva/treesim/team*:
  3720. <xmp highlight=json-ld line-highlight=4>
  3721. {
  3722. "@context": "https://www.w3.org/ns/activitystreams",
  3723. "id": "https://dev.example/aviva/treesim/team",
  3724. "type": "Collection",
  3725. "totalItems": 3,
  3726. "items": [
  3727. "https://dev.example/aviva",
  3728. "https://dev.example/luke",
  3729. "https://code.community/users/lorax"
  3730. ]
  3731. }
  3732. </xmp>
  3733. </div>
  3734. ### Tracker linking
  3735. <pre class=simpledef>
  3736. Name: <dfn dfn export>ticketsTrackedBy</dfn>
  3737. URI: https://forgefed.org/ns#ticketsTrackedBy
  3738. Domain: [=Object=]
  3739. Range: [=Object=] that is an actor
  3740. Functional: Yes
  3741. Inverse of: [=tracksTicketsFor=]
  3742. Description:
  3743. Identifies the actor which tracks tickets related to the given object. This
  3744. is the actor to whom you send tickets you'd like to open against the
  3745. object.
  3746. </pre>
  3747. <div class=example>
  3748. <xmp highlight=json-ld line-highlight=10>
  3749. {
  3750. "@context": [
  3751. "https://www.w3.org/ns/activitystreams",
  3752. "https://forgefed.org/ns"
  3753. ],
  3754. "id": "https://dev.example/aviva/treesim",
  3755. "type": "Repository",
  3756. "name": "Tree Growth 3D Simulation",
  3757. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>",
  3758. "ticketsTrackedBy": "https://bugs.example/projects/treesim"
  3759. }
  3760. </xmp>
  3761. </div>
  3762. <pre class=simpledef>
  3763. Name: <dfn dfn export>tracksTicketsFor</dfn>
  3764. URI: https://forgefed.org/ns#tracksTicketsFor
  3765. Domain: [=Object=] that is an actor
  3766. Range: [=Object=]
  3767. Functional: No
  3768. Inverse of: [=ticketsTrackedBy=]
  3769. Description:
  3770. Identifies objects for which which this ticket tracker tracks tickets. When
  3771. you'd like to open a ticket against those objects, you can send them to
  3772. this tracker.
  3773. </pre>
  3774. <div class=example>
  3775. <xmp highlight=json-ld line-highlight=8>
  3776. {
  3777. "@context": [
  3778. "https://www.w3.org/ns/activitystreams",
  3779. "https://forgefed.org/ns"
  3780. ],
  3781. "id": "https://bugs.example/treesim",
  3782. "type": "Project",
  3783. "tracksTicketsFor": [
  3784. "https://dev.example/aviva/liblsystem",
  3785. "https://dev.example/aviva/3d-tree-models",
  3786. "https://dev.example/aviva/treesim"
  3787. ]
  3788. }
  3789. </xmp>
  3790. </div>
  3791. <pre class=simpledef>
  3792. Name: <dfn dfn export>sendPatchesTo</dfn>
  3793. URI: https://forgefed.org/ns#sendPatchesTo
  3794. Domain: [=Repository=]
  3795. Range: [=PatchTracker=]
  3796. Functional: Yes
  3797. Inverse of: [=tracksPatchesFor=]
  3798. Description:
  3799. Identifies the [=PatchTracker=] which tracks patches and merge requests
  3800. related to the given repository. This is the actor to whom you send patches
  3801. and merge requests you'd like to open against the repository.
  3802. </pre>
  3803. <div class=example>
  3804. <xmp highlight=json-ld line-highlight=10>
  3805. {
  3806. "@context": [
  3807. "https://www.w3.org/ns/activitystreams",
  3808. "https://forgefed.org/ns"
  3809. ],
  3810. "id": "https://dev.example/repos/treesim",
  3811. "type": "Repository",
  3812. "name": "Tree Growth 3D Simulation",
  3813. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>",
  3814. "sendPatchesTo": "https://bugs.example/pr-trackers/treesim"
  3815. }
  3816. </xmp>
  3817. </div>
  3818. <pre class=simpledef>
  3819. Name: <dfn dfn export>tracksPatchesFor</dfn>
  3820. URI: https://forgefed.org/ns#tracksPatchesFor
  3821. Domain: [=PatchTracker=]
  3822. Range: [=Repository=]
  3823. Functional: No
  3824. Inverse of: [=sendPatchesTo=]
  3825. Description:
  3826. Identifies a repository for which which this patch and merge request
  3827. tracker tracks patches and merge requests. When you'd like to open patches
  3828. or merge requests against that repository, you can send them to this
  3829. tracker.
  3830. </pre>
  3831. <div class=example>
  3832. <xmp highlight=json-ld line-highlight=8>
  3833. {
  3834. "@context": [
  3835. "https://www.w3.org/ns/activitystreams",
  3836. "https://forgefed.org/ns"
  3837. ],
  3838. "id": "https://project.example/treesim",
  3839. "type": "PatchTracker",
  3840. "tracksPatchesFor": [
  3841. "https://dev.example/aviva/liblsystem",
  3842. "https://dev.example/aviva/3d-tree-models",
  3843. "https://dev.example/aviva/treesim"
  3844. ]
  3845. }
  3846. </xmp>
  3847. </div>
  3848. ### Repository forking
  3849. <pre class=simpledef>
  3850. Name: <dfn dfn export>forkedFrom</dfn>
  3851. URI: https://forgefed.org/ns#forkedFrom
  3852. Domain: [=Repository=]
  3853. Range: [=Repository=]
  3854. Functional: Yes
  3855. Inverse of: [=forks=]
  3856. Description:
  3857. Identifies the [=Repository=] which this [=Repository=] was created as a
  3858. fork of, i.e. by cloning it.
  3859. </pre>
  3860. <div class=example>
  3861. <xmp highlight=json-ld line-highlight=8>
  3862. {
  3863. "@context": [
  3864. "https://www.w3.org/ns/activitystreams",
  3865. "https://forgefed.org/ns"
  3866. ],
  3867. "id": "https://example.dev/alice/myfork/",
  3868. "type": "Repository",
  3869. "forkedFrom": {
  3870. "type": "Repository",
  3871. "id": "https://example.dev/luke/myrepo/"
  3872. }
  3873. }
  3874. </xmp>
  3875. </div>
  3876. <pre class=simpledef>
  3877. Name: <dfn dfn export>forks</dfn>
  3878. URI: https://forgefed.org/ns#forks
  3879. Domain: [=Repository=]
  3880. Range: [=OrderedCollection=] of items of type [=Repository=]
  3881. Functional: Yes
  3882. Inverse of: [=forkedFrom=]
  3883. Description:
  3884. Identifies an [=OrderedCollection=] of [=Repository=]s which were created
  3885. as forks of this [=Repository=], i.e. by cloning it. The order of the
  3886. collection items is by reverse chronological order of the forking events.
  3887. </pre>
  3888. <div class=example>
  3889. <xmp highlight=json-ld line-highlight=8>
  3890. {
  3891. "@context": [
  3892. "https://www.w3.org/ns/activitystreams",
  3893. "https://forgefed.org/ns"
  3894. ],
  3895. "id": "https://example.dev/luke/myrepo/",
  3896. "type": "Repository",
  3897. "forks": {
  3898. "type": "OrderedCollection",
  3899. "totalItems": 1,
  3900. "orderedItems": [
  3901. {
  3902. "id": "https://example.dev/alice/myfork/",
  3903. "type": "Repository",
  3904. }
  3905. ]
  3906. },
  3907. }
  3908. </xmp>
  3909. </div>
  3910. ### Access control
  3911. <pre class=simpledef>
  3912. Name: <dfn dfn export>fulfills</dfn>
  3913. URI: https://forgefed.org/ns#fulfills
  3914. Domain: [=Activity=]
  3915. Range: [=Activity=]
  3916. Functional: No
  3917. Inverse of: None
  3918. Description:
  3919. For an activity *A* that `fulfills` some other activity *B*, specifies that
  3920. *A* has been published as part of fulfilling the action requested by *B*.
  3921. For example, if Alice creates a new repository using `Create`, she may want
  3922. to instantly automatically start following this new repository using
  3923. `Follow` (to be notified when her friends push commits there). This
  3924. `Follow` `fulfills` the `Create`; it's an activity automatically sent as
  3925. part of creating a new repository.
  3926. </pre>
  3927. <div class=example>
  3928. <xmp highlight=json-ld line-highlight=9>
  3929. TODO
  3930. </xmp>
  3931. </div>
  3932. <pre class=simpledef>
  3933. Name: <dfn dfn export>allows</dfn>
  3934. URI: https://forgefed.org/ns#allows
  3935. Domain: [=Grant=]
  3936. Range: [=CapabilityUsage=]
  3937. Functional: No
  3938. Inverse of: None
  3939. Description:
  3940. Specifies which modes of using this [=Grant=] are being allowd by it. The
  3941. two conceptual operations that `Grant`s support are invocation (acting on
  3942. the resource under the specified role) and delegation (passing on the
  3943. access to more actors, possibly with reduced privileges). This property
  3944. specifies which of these operations are supported, and under which
  3945. conditions. See [=CapabilityUsage=] for specific values to use.
  3946. </pre>
  3947. <div class=example>
  3948. <xmp highlight=json-ld line-highlight=9>
  3949. TODO
  3950. </xmp>
  3951. </div>
  3952. <pre class=simpledef>
  3953. Name: <dfn dfn export>capability</dfn>
  3954. URI: https://forgefed.org/ns#capability
  3955. Domain: [=Activity=]
  3956. Range: [=Grant=]
  3957. Functional: Yes
  3958. Inverse of: None
  3959. Description:
  3960. Specifies a previously published [=Grant=] activity providing relevant
  3961. access permissions. For example, if Alice wants to resolve a [=Ticket=]
  3962. under some project, she will send an activity with `capability` referring
  3963. to the `Grant` activity that gave her collaborator access to that project,
  3964. which happens to include permission to resolve tickets.
  3965. </pre>
  3966. <div class=example>
  3967. <xmp highlight=json-ld line-highlight=9>
  3968. TODO
  3969. </xmp>
  3970. </div>
  3971. <pre class=simpledef>
  3972. Name: <dfn dfn export>managedBy</dfn>
  3973. URI: https://forgefed.org/ns#managedBy
  3974. Domain: [=Object=]
  3975. Range: [=Object=] that is an actor
  3976. Functional: Yes
  3977. Inverse of: None
  3978. Description:
  3979. Identifies the actor that controls the given resource, and to whom
  3980. activities asking to modify the resource may be submitted.
  3981. </pre>
  3982. <div class=example>
  3983. <xmp highlight=json-ld line-highlight=9>
  3984. {
  3985. "@context": [
  3986. "https://www.w3.org/ns/activitystreams",
  3987. "https://forgefed.org/ns"
  3988. ],
  3989. "type": "Ticket",
  3990. "id": "https://example.dev/alice/myrepo/issues/42",
  3991. "context": "https://example.dev/alice/myrepo",
  3992. "managedBy": "https://example.dev/alice/myrepo",
  3993. "attributedTo": "https://dev.community/bob",
  3994. "summary": "Nothing works!",
  3995. "content": "<p>Please fix. <i>Everything</i> is broken!</p>",
  3996. "mediaType": "text/html",
  3997. "source": {
  3998. "content": "Please fix. *Everything* is broken!",
  3999. "mediaType": "text/markdown; variant=CommonMark"
  4000. },
  4001. "isResolved": false
  4002. }
  4003. </xmp>
  4004. </div>
  4005. <pre class=simpledef>
  4006. Name: <dfn dfn export>delegates</dfn>
  4007. URI: https://forgefed.org/ns#delegates
  4008. Domain: [=Grant=]
  4009. Range: [=Grant=]
  4010. Functional: Yes
  4011. Inverse of: None
  4012. Description:
  4013. Actors can use [=Grant=] activities to allow other actors to access their
  4014. resources. They can also allow those other actors to pass on (delegate)
  4015. this access to even more actors. For a `Grant` that delegates access
  4016. provided by an earlier `Grant`, the former uses `delegates` to specify the
  4017. latter. That earlier `Grant` is also called the "parent capability" of this
  4018. `Grant`.
  4019. </pre>
  4020. <div class=example>
  4021. <xmp highlight=json-ld line-highlight=9>
  4022. TODO
  4023. </xmp>
  4024. </div>
  4025. ### Repository mirroring
  4026. <pre class=simpledef>
  4027. Name: <dfn dfn export>mirrors</dfn>
  4028. URI: https://forgefed.org/ns#mirrors
  4029. Domain: [=Repository=]
  4030. Range: [=Repository=]
  4031. Functional: Yes
  4032. Inverse of: [=mirroredBy=]
  4033. Description:
  4034. Identifies the [=Repository=] which this [=Repository=] copies content from
  4035. (i.e. what this repository is a "pull mirror" of).
  4036. </pre>
  4037. <div class=example>
  4038. <xmp highlight=json-ld line-highlight=8>
  4039. {
  4040. "@context": [
  4041. "https://www.w3.org/ns/activitystreams",
  4042. "https://forgefed.org/ns"
  4043. ],
  4044. "id": "https://example.dev/alice/mymirror/",
  4045. "type": "Repository",
  4046. "mirrors": {
  4047. "type": "Repository",
  4048. "id": "https://example.dev/luke/myrepo/"
  4049. }
  4050. }
  4051. </xmp>
  4052. </div>
  4053. <pre class=simpledef>
  4054. Name: <dfn dfn export>mirroredBy</dfn>
  4055. URI: https://forgefed.org/ns#mirroredBy
  4056. Domain: [=Repository=]
  4057. Range: [=Repository=]
  4058. Functional: No
  4059. Inverse of: [=mirrors=]
  4060. Description:
  4061. Identifies a [=Repository=] which copies content from this repository (i.e.
  4062. "pull mirror" of this repository).
  4063. </pre>
  4064. <div class=example>
  4065. <xmp highlight=json-ld line-highlight=8>
  4066. {
  4067. "@context": [
  4068. "https://www.w3.org/ns/activitystreams",
  4069. "https://forgefed.org/ns"
  4070. ],
  4071. "id": "https://example.dev/luke/myrepo/",
  4072. "type": "Repository",
  4073. "mirroredBy": {
  4074. "type": "Repository",
  4075. "id": "https://example.dev/alice/mymirror/"
  4076. }
  4077. }
  4078. </xmp>
  4079. </div>
  4080. <pre class=simpledef>
  4081. Name: <dfn dfn export>mirrorsTo</dfn>
  4082. URI: https://forgefed.org/ns#mirrorsTo
  4083. Domain: [=Repository=]
  4084. Range: [=Repository=]
  4085. Functional: No
  4086. Inverse of: [=mirroredFrom=]
  4087. Description:
  4088. Identifies a [=Repository=] which this repository copies content to (i.e.
  4089. "push mirror" of this repository)
  4090. </pre>
  4091. <div class=example>
  4092. <xmp highlight=json-ld line-highlight=8>
  4093. {
  4094. "@context": [
  4095. "https://www.w3.org/ns/activitystreams",
  4096. "https://forgefed.org/ns"
  4097. ],
  4098. "id": "https://example.dev/alice/myrepo/",
  4099. "type": "Repository",
  4100. "mirrorsTo": {
  4101. "type": "Repository",
  4102. "id": "https://example.dev/alice-backup/myrepo/"
  4103. }
  4104. }
  4105. </xmp>
  4106. </div>
  4107. <pre class=simpledef>
  4108. Name: <dfn dfn export>mirroredFrom</dfn>
  4109. URI: https://forgefed.org/ns#mirroredFrom
  4110. Domain: [=Repository=]
  4111. Range: [=Repository=]
  4112. Functional: Yes
  4113. Inverse of: [=mirrorsTo=]
  4114. Description:
  4115. Identifies the [=Repository=] which copies its content to this
  4116. [=Repository=] (ie. what this repository is a "push mirror" of).
  4117. </pre>
  4118. <div class=example>
  4119. <xmp highlight=json-ld line-highlight=8>
  4120. {
  4121. "@context": [
  4122. "https://www.w3.org/ns/activitystreams",
  4123. "https://forgefed.org/ns"
  4124. ],
  4125. "id": "https://example.dev/alice-backup/myrepo/",
  4126. "type": "Repository",
  4127. "mirroredFrom": {
  4128. "type": "Repository",
  4129. "id": "https://example.dev/alice/myrepo/"
  4130. }
  4131. }
  4132. </xmp>
  4133. </div>
  4134. ## Values
  4135. ### Capability uses
  4136. <pre class=simpledef>
  4137. Name: <dfn dfn export>gatherAndConvey</dfn>
  4138. URI: https://forgefed.org/ns#gatherAndConvey
  4139. Type: [=CapabilityUsage=]
  4140. Conditions:
  4141. <ul>
  4142. <li>
  4143. The `Grant`'s [=target=] MUST be a [=Project=]
  4144. </li>
  4145. <li>
  4146. It may delegate the `Grant`, allowing only `gatherAndConvey`, to
  4147. parent projects
  4148. </li>
  4149. <li>
  4150. It may delegate the `Grant`, allowing only `distribute`, to teams
  4151. to which it allows to access it
  4152. </li>
  4153. <li>
  4154. It may delegate the `Grant`, allowing `invoke` only, to people to
  4155. which it allows to access it
  4156. </li>
  4157. </ul>
  4158. </pre>
  4159. <pre class=simpledef>
  4160. Name: <dfn dfn export>distribute</dfn>
  4161. URI: https://forgefed.org/ns#distribute
  4162. Type: [=CapabilityUsage=]
  4163. Conditions:
  4164. <ul>
  4165. <li>
  4166. The `Grant`'s [=target=] MUST be a [=Team=]
  4167. </li>
  4168. <li>
  4169. It may delegate the `Grant`, allowing `distribute` only, to its
  4170. subteams
  4171. </li>
  4172. <li>
  4173. It may delegate the `Grant`, allowing `invoke` only, to its members
  4174. </li>
  4175. </ul>
  4176. </pre>
  4177. <pre class=simpledef>
  4178. Name: <dfn dfn export>invoke</dfn>
  4179. URI: https://forgefed.org/ns#invoke
  4180. Type: [=CapabilityUsage=]
  4181. Conditions:
  4182. <ul>
  4183. <li>
  4184. The `Grant`'s [=target=] may invoke it, i.e. use it as the
  4185. [=capability=] in another activity, that requests to access or
  4186. modify the resource specified by the `Grant`'s [=context=]
  4187. </li>
  4188. </ul>
  4189. </pre>
  4190. ### Roles
  4191. <pre class=simpledef>
  4192. Name: <dfn dfn export>visit</dfn>
  4193. URI: https://forgefed.org/ns#visit
  4194. Type: [=Role=]
  4195. Description:
  4196. Authorizes the [=Grant=] recipient (i.e. [=target=]) to view the [=Grant=]
  4197. resource (i.e. [=context=]), which includes retrieving objects via HTTP and
  4198. pulling/cloning VCS repos
  4199. </pre>
  4200. <pre class=simpledef>
  4201. Name: <dfn dfn export>report</dfn>
  4202. URI: https://forgefed.org/ns#report
  4203. Type: [=Role=]
  4204. Description:
  4205. Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
  4206. resource (i.e. [=context=]) anything that the [=visit=] role authorizes,
  4207. and also to do basic community participation tasks:
  4208. [Open an issue](#opening-issue),
  4209. [submit a PR](#opening-mr),
  4210. [create comments](#commenting)
  4211. and discussion threads,
  4212. edit public wikis,
  4213. submit PR reviews.
  4214. </pre>
  4215. <pre class=simpledef>
  4216. Name: <dfn dfn export>triage</dfn>
  4217. URI: https://forgefed.org/ns#triage
  4218. Type: [=Role=]
  4219. Description:
  4220. Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
  4221. resource (i.e. [=context=]) anything that the [=report=] role authorizes,
  4222. and also to edit issue/PR propeties (labels, milestones, due dates, etc.),
  4223. close and reopen issues and PRs, assign and unassign people to issues and
  4224. PRs, request PR reviews, hide disruptive comments (a moderation action),
  4225. lock and move discussions.
  4226. </pre>
  4227. <pre class=simpledef>
  4228. Name: <dfn dfn export>write</dfn>
  4229. URI: https://forgefed.org/ns#write
  4230. Type: [=Role=]
  4231. Description:
  4232. Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
  4233. resource (i.e. [=context=]) anything that the [=triage=] role authorizes,
  4234. and also to
  4235. apply PR suggested changes,
  4236. edit non-public wikis,
  4237. create/edit/delete labels,
  4238. merge a PR,
  4239. [push to VCS repositories](#pushing),
  4240. create/edit/run/cancel CI recipes,
  4241. manage releases,
  4242. publish packages,
  4243. create web IDE coding sessions.
  4244. </pre>
  4245. <pre class=simpledef>
  4246. Name: <dfn dfn export>maintain</dfn>
  4247. URI: https://forgefed.org/ns#maintain
  4248. Type: [=Role=]
  4249. Description:
  4250. Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
  4251. resource (i.e. [=context=]) anything that the [=write=] role authorizes,
  4252. and also to edit project and component descriptions and settings unrelated
  4253. to access, enable/disable components, configure "Pages" publishing of
  4254. static websites from repos, push to repos' protected branches.
  4255. </pre>
  4256. <pre class=simpledef>
  4257. Name: <dfn dfn export>admin</dfn>
  4258. URI: https://forgefed.org/ns#admin
  4259. Type: [=Role=]
  4260. Description:
  4261. Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
  4262. resource (i.e. [=context=]) anything that the [=maintain=] role authorizes,
  4263. and also to
  4264. [manage access to projects, components and teams](#managing-access),
  4265. merge PRs even without reviews,
  4266. delete issues,
  4267. change project/component/team visibility,
  4268. edit project/component/team access-related settings,
  4269. change a repo's default branch,
  4270. manage webhooks and deployment,
  4271. move components and projects between projects,
  4272. archive projects/components,
  4273. delete components/projects/teams.
  4274. </pre>
  4275. <pre class=simpledef>
  4276. Name: <dfn dfn export>delegate</dfn>
  4277. URI: https://forgefed.org/ns#delegate
  4278. Type: [=Role=]
  4279. Description:
  4280. Authorizes the [=Grant=] recipient (i.e. [=target=]) to send access
  4281. delegations to the [=Grant=] sender (i.e. [=actor=])
  4282. </pre>
  4283. <pre class=biblio>
  4284. {
  4285. "wikipedia-ticket-tracker": {
  4286. "href": "https://en.wikipedia.org/wiki/Issue_tracking_system",
  4287. "title": "Wikipedia: Issue tracking system"
  4288. },
  4289. "wikipedia-pr": {
  4290. "href": "https://en.wikipedia.org/wiki/Distributed_version_control#Pull_requests",
  4291. "title": "Wikipedia: Pull requests"
  4292. },
  4293. "fep-8b32": {
  4294. "href": "https://codeberg.org/fediverse/fep/src/branch/main/feps/fep-8b32.md",
  4295. "title": "FEP-8b32: Object Integrity Proofs"
  4296. }
  4297. }
  4298. </pre>
  4299. <pre class=anchors>
  4300. urlPrefix: https://www.w3.org/TR/xmlschema11-2/#; type: dfn; spec: xmlschema11-2
  4301. text: dateTime
  4302. urlPrefix: http://purl.org/dc/terms/; type: dfn; spec: dcterms
  4303. text: created
  4304. urlPrefix: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-; type: dfn; spec: activitystreams-vocabulary
  4305. text: Accept
  4306. text: Add
  4307. text: Create
  4308. text: Invite
  4309. text: Join
  4310. text: Leave
  4311. text: Offer
  4312. text: Reject
  4313. text: Remove
  4314. text: Undo
  4315. urlPrefix: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-; type: dfn; spec: activitystreams-vocabulary
  4316. text: Activity
  4317. text: Collection
  4318. text: Image
  4319. text: Link
  4320. text: Note
  4321. text: Object
  4322. text: OrderedCollection
  4323. text: Person
  4324. urlPrefix: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-; type: dfn; spec: activitystreams-vocabulary
  4325. text: actor
  4326. text: attachment
  4327. text: attributedTo
  4328. text: content
  4329. text: context
  4330. text: duration
  4331. text: endTime
  4332. text: id
  4333. text: inbox
  4334. text: inReplyTo
  4335. text: instrument
  4336. text: items
  4337. text: mediaType
  4338. text: name
  4339. text: object
  4340. text: orderedItems
  4341. text: origin
  4342. text: published
  4343. text: relationship
  4344. text: replies
  4345. text: result
  4346. text: source
  4347. text: startTime
  4348. text: subject
  4349. text: summary
  4350. text: tag
  4351. text: target
  4352. text: to
  4353. text: type
  4354. urlPrefix: https://www.w3.org/TR/activitypub/#; type: dfn; spec: activitypub
  4355. text: followers
  4356. </pre>