spec.bs 188 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306
  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 #### {#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. ## Adding and Removing Children and Parents to Projects and Teams
  1580. Minimal required role: [=admin=]
  1581. Like most cooperation between ForgeFed actors, the parent-child link is based
  1582. on mutual consent:
  1583. - To form the link, both projects/teams must explicitly agree to it
  1584. - To remove the link, it's enough for one of them to withdraw its consent
  1585. Parent-child links are a part of the Grant delegation system, in which the flow
  1586. is:
  1587. - Components initiate delegation, to:
  1588. - The projects they belong to
  1589. - Teams which have access to them
  1590. - Projects delegate to their:
  1591. - Direct collaborators
  1592. - Parent projects
  1593. - Teams which have access to them
  1594. - Teams delegate to their:
  1595. - Direct collaborators
  1596. - Child teams
  1597. Once a parent-child link is formed, delegations start happening through the
  1598. link. This is described in more detail below.
  1599. Each delegation within this flow therefore has a "source" side, which sends the
  1600. delegation, and a "destination" side, which receives it and possibly delegates
  1601. it further.
  1602. In a parent-child link, therefore, there is a "source" side and a "destination"
  1603. side. For projects, the child is the source and the parent is the destination
  1604. (because projects delegate to their parents, once a link is formed). For teams,
  1605. the parent is the source and the child is the destination (because teams
  1606. delegate to their children, once a link is formed).
  1607. Since either side of the link can initiate the process, and since delegation
  1608. flow occurs in one direction, there are essentially two versions of the
  1609. process:
  1610. - Process initiated by the source side (i.e. a project asking to have a parent,
  1611. or a team asking to have a child)
  1612. - Process initiated by the destination side (i.e. a project asking to have a
  1613. child, or a team asking to have a parent)
  1614. In the [=Add=] activity that initiates the process, the initiating side is
  1615. determined by the [=target=] property. The actor to whom the collection
  1616. specified by [=target=] belongs, is the initiating side. The other side of the
  1617. link is specified in the [=object=] property.
  1618. Whenever authorization is mentioned below, it SHOULD be done using the
  1619. [[#s2s-grant-flow|invocation verification process]].
  1620. ### Forming a project child-parent link
  1621. #### Initiated by the parent
  1622. Assuming:
  1623. - A project *P*
  1624. - A project *C*
  1625. - A person with admin access to *C*, Celia
  1626. - A person with admin access to *P*, Philip
  1627. Philip wants *C* to become a child of *P* (which means *P* will be a parent
  1628. of *C*). The exchange of activities SHOULD be as follows:
  1629. 1. Philip sends an [=Add=] activity in which:
  1630. - [=object=] is *C*
  1631. - [=target=] is the URI of *P*'s [=subprojects=] (i.e. children) collection
  1632. - [=instrument=] is the maximal [=Role=] that Philip would like to allow
  1633. for people (and bots) when authorizing their manipulation of *C* by
  1634. their access in *P* (normally it would be [=admin=], perhaps sometimes
  1635. [=maintain=])
  1636. - [=capability=] is the URI of a [=Grant=] that authorizes Philip's admin
  1637. access to *P*
  1638. 2. *P*, seeing and authorizing the Add, publishes an [=Accept=] where
  1639. [=object=] is the Add's URI
  1640. 3. Celia, seeing the Add and the Accept, sends an [=Accept=] where:
  1641. - [=object=] is the Add's URI
  1642. - [=capability=] is the URI of a [=Grant=] that authorizes Celia's admin
  1643. access to *C*
  1644. 4. *C*, seeing the Add and authorizing Celia's Accept, publishes an [=Accept=]
  1645. where [=object=] is the Add's URI
  1646. 5. *P*, seeing *C*'s Accept, publishes a [[#grant-delegate|delegate-Grant]] in
  1647. which the [=target=] is *C*, and now considers *C* a child and lists *C* in
  1648. the [=subprojects=] collection
  1649. 6. *C*, seeing *P*'s Grant, now considers *P* a parent and lists *P* in the
  1650. [=context=] (i.e. parents) collection
  1651. Now *C* sends delegation Grant activities, see the [[#deleg-step-j]] section
  1652. below.
  1653. #### Initiated by the child
  1654. Assuming:
  1655. - A project *P*
  1656. - A project *C*
  1657. - A person with admin access to *C*, Celia
  1658. - A person with admin access to *P*, Philip
  1659. Celia wants *C* to become a child of *P* (which means *P* will be a parent
  1660. of *C*). The exchange of activities SHOULD be as follows:
  1661. 1. Celia sends an [=Add=] activity in which:
  1662. - [=object=] is *P*
  1663. - [=target=] is the URI of *C*'s [=context=] (i.e. parents) collection
  1664. - [=instrument=] is the maximal [=Role=] that Celia would like to allow
  1665. for people (and bots) when authorizing their manipulation of *C* by
  1666. their access in *P* (normally it would be [=admin=], perhaps sometimes
  1667. [=maintain=])
  1668. - [=capability=] is the URI of a [=Grant=] that authorizes Celia's admin
  1669. access to *C*
  1670. 2. *C*, seeing and authorizing the Add, publishes an [=Accept=] where
  1671. [=object=] is the Add's URI
  1672. 3. Philip, seeing the Add and the Accept, sends an [=Accept=] where:
  1673. - [=object=] is the Add's URI
  1674. - [=capability=] is the URI of a [=Grant=] that authorizes Philip's admin
  1675. access to *P*
  1676. 4. *P*, seeing the Add, seeing *C*'s Accept, and authorizing Philip's Accept,
  1677. publishes a [[#grant-delegate|delegate-Grant]] in which the [=target=] is
  1678. *C*, and now considers *C* a child and lists *C* in the [=subprojects=]
  1679. collection
  1680. 5. *C*, seeing *P*'s Grant, now considers *P* a parent and lists *P* in the
  1681. [=context=] (i.e. parents) collection
  1682. Now *C* sends delegation Grant activities, see the [[#deleg-step-j]] section
  1683. below.
  1684. #### Delegation step #### {#deleg-step-j}
  1685. *C* now sends Grant activities, which *P* delegates further by sending its own
  1686. Grant activities, as described in [[#extending-a-delegation-chain]], to its
  1687. member humans, member teams, and parent projects.
  1688. 1. *C* sends a [[#start-grant-chain|start-Grant]] where:
  1689. - [=actor=] and [=context=] specify *C*
  1690. - [=target=] specifies *P*
  1691. - [=object=] specifies the role specified in the Add's [=instrument=]
  1692. - [=allows=] is [=gatherAndConvey=]
  1693. - [=capability=] is the URI of the delegate-Grant from step 5 or 4 above
  1694. 2. For each delegation Grant *g* that *C* has received from its components and
  1695. child projects, *C* sends an
  1696. [[#extending-a-delegation-chain|extension-Grant]] where:
  1697. - [=actor=] specifies *C*
  1698. - [=context=] is identical to *g*'s [=context=]
  1699. - [=target=] specifies *P*
  1700. - [=object=] specifies the lower role among the one specified by the
  1701. Add's [=instrument=], and the one specified by *g*'s [=object=]
  1702. - [=allows=] is [=gatherAndConvey=]
  1703. - [=capability=] is the URI of the delegate-Grant from step 5 or 4
  1704. above
  1705. - [=delegates=] specifies *g*'s URI
  1706. When *P* sees these Grants, it delegates them further, as mentioned above.
  1707. In addition, as long as the parent-child link remains active, whenever *C*
  1708. receives a delegation Grant from any of its components or children, it SHOULD
  1709. send *P* an extension-Grant as described above, and *P* SHOULD extend it
  1710. further.
  1711. ### Removing a project child-parent link
  1712. #### Initiated by the child
  1713. Assuming:
  1714. - Projects *P* and *C*, where *P* is a parent of *C*
  1715. - A person with admin access to *C*, Celia
  1716. Celia wants *C* to stop being a child of *P* (which means *P* will stop
  1717. being a parent of *C*). The exchange of activities SHOULD be as follows:
  1718. 1. Celia sends an [=Remove=] activity in which:
  1719. - [=object=] is *P*
  1720. - [=origin=] is the URI of *C*'s [=context=] (i.e. parents) collection
  1721. - [=capability=] is the URI of a [=Grant=] that authorizes Celia's admin
  1722. access to *C*
  1723. 2. *C*, seeing and authorizing the Remove, publishes an [=Accept=] where
  1724. [=object=] is the Add's URI
  1725. 3. *P*, seeing the Remove and the Accept, publishes a [=Revoke=] activity in
  1726. which the [=object=] is the delegate-Grant it had sent to *C*, and now
  1727. considers that Grant revoked, and removes *C* from its [=subprojects=]
  1728. collection
  1729. 4. *C*, seeing *P*'s Revoke, removes *P* from its [=context=] (i.e. parents)
  1730. collection
  1731. Now, for each Grant *g* that *P* has published, as a extension of a Grant it
  1732. had received from *C*:
  1733. 1. *P* sends (to *g*'s [=target=]) a [=Revoke=] activity whose [=object=]
  1734. specifies *g*'s URI
  1735. 2. *P* considers *g* invalidated
  1736. 3. The recipient of the [=Revoke=], if it further published extensions of *g*,
  1737. now similarly publishes Revoke activities and considers those extension
  1738. Grants invalid
  1739. #### Initiated by the parent
  1740. Assuming:
  1741. - Projects *P* and *C*, where *P* is a parent of *C*
  1742. - A person with admin access to *P*, Philip
  1743. Philip wants *C* to stop being a child of *P* (which means *P* will stop
  1744. being a parent of *C*). The exchange of activities SHOULD be as follows:
  1745. 1. Philip sends an [=Remove=] activity in which:
  1746. - [=object=] is *C*
  1747. - [=origin=] is the URI of *P*'s [=subprojects=] (i.e. children) collection
  1748. - [=capability=] is the URI of a [=Grant=] that authorizes Philip's admin
  1749. access to *P*
  1750. 2. *P*, seeing and authorizing the Remove, publishes a [=Revoke=] activity in
  1751. which the [=object=] is the delegate-Grant it had sent to *C*, and now
  1752. considers that Grant revoked, and removes *C* from its [=subprojects=]
  1753. collection
  1754. 3. *C*, seeing *P*'s Revoke, removes *P* from its [=context=] (i.e. parents)
  1755. collection, and MAY publish an Accept (whose [=object=] is the Remove) to
  1756. notify its followers
  1757. Now, for each Grant *g* that *P* has published, as a extension of a Grant it
  1758. had received from *C*:
  1759. 1. *P* sends (to *g*'s [=target=]) a [=Revoke=] activity whose [=object=]
  1760. specifies *g*'s URI
  1761. 2. *P* considers *g* invalidated
  1762. 3. The recipient of the [=Revoke=], if it further published extensions of *g*,
  1763. now similarly publishes Revoke activities and considers those extension
  1764. Grants invalid
  1765. ### Forming a team child-parent link
  1766. #### Initiated by the parent
  1767. Assuming:
  1768. - A team *P*
  1769. - A team *C*
  1770. - A person with admin access to *C*, Celia
  1771. - A person with admin access to *P*, Philip
  1772. Philip wants *C* to become a child of *P* (which means *P* will be a parent
  1773. of *C*). The exchange of activities SHOULD be as follows:
  1774. 1. Philip sends an [=Add=] activity in which:
  1775. - [=object=] is *C*
  1776. - [=target=] is the URI of *P*'s [=subteams=] (i.e. children) collection
  1777. - [=instrument=] is the maximal [=Role=] that Philip would like to allow
  1778. for people (and bots) when authorizing their manipulation of *C* by
  1779. their access in *P* (normally it would be [=admin=], perhaps sometimes
  1780. [=maintain=])
  1781. - [=capability=] is the URI of a [=Grant=] that authorizes Philip's admin
  1782. access to *P*
  1783. 2. *P*, seeing and authorizing the Add, publishes an [=Accept=] where
  1784. [=object=] is the Add's URI
  1785. 3. Celia, seeing the Add and the Accept, sends an [=Accept=] where:
  1786. - [=object=] is the Add's URI
  1787. - [=capability=] is the URI of a [=Grant=] that authorizes Celia's admin
  1788. access to *C*
  1789. 4. *C*, seeing the Add, seeing *P*'s Accept, and authorizing Celia's Accept,
  1790. publishes a [[#grant-delegate|delegate-Grant]] in which the [=target=] is
  1791. *P*, and now considers *P* a parent and lists *P* in the [=context=] (i.e.
  1792. parents) collection
  1793. 5. *P*, seeing *C*'s Grant, now considers *C* a parent and lists *C* in the
  1794. [=subteams=] collection
  1795. Now *P* sends delegation Grant activities, see the [[#deleg-step-t]] section
  1796. below.
  1797. #### Initiated by the child
  1798. Assuming:
  1799. - A team *P*
  1800. - A team *C*
  1801. - A person with admin access to *C*, Celia
  1802. - A person with admin access to *P*, Philip
  1803. Celia wants *C* to become a child of *P* (which means *P* will be a parent
  1804. of *C*). The exchange of activities SHOULD be as follows:
  1805. 1. Celia sends an [=Add=] activity in which:
  1806. - [=object=] is *P*
  1807. - [=target=] is the URI of *C*'s [=context=] (i.e. parents) collection
  1808. - [=instrument=] is the maximal [=Role=] that Celia would like to allow
  1809. for people (and bots) when authorizing their manipulation of *C* by
  1810. their access in *P* (normally it would be [=admin=], perhaps sometimes
  1811. [=maintain=])
  1812. - [=capability=] is the URI of a [=Grant=] that authorizes Celia's admin
  1813. access to *C*
  1814. 2. *C*, seeing and authorizing the Add, publishes an [=Accept=] where
  1815. [=object=] is the Add's URI
  1816. 3. Philip, seeing the Add and the Accept, sends an [=Accept=] where:
  1817. - [=object=] is the Add's URI
  1818. - [=capability=] is the URI of a [=Grant=] that authorizes Philip's admin
  1819. access to *P*
  1820. 4. *P*, seeing the Add, seeing *C*'s Accept, and authorizing Philip's Accept,
  1821. publishes an [=Accept=] where [=object=] is the Add's URI
  1822. 5. *C*, seeing *P*'s Accept, publishes a [[#grant-delegate|delegate-Grant]] in
  1823. which the [=target=] is *P*, and now considers *P* a parent and lists *P*
  1824. in the [=context=] (i.e. parents) collection
  1825. 6. *P*, seeing *C*'s Grant, now considers *C* a child and lists *C* in the
  1826. [=subteams=] collection
  1827. Now *P* sends delegation Grant activities, see the [[#deleg-step-t]] section
  1828. below.
  1829. #### Delegation step #### {#deleg-step-t}
  1830. *P* now sends Grant activities, which *C* delegates further by sending its own
  1831. Grant activities, as described in [[#extending-a-delegation-chain]], to its
  1832. member humans and child teams.
  1833. 1. *P* sends a [[#start-grant-chain|start-Grant]] where:
  1834. - [=actor=] and [=context=] specify *P*
  1835. - [=target=] specifies *C*
  1836. - [=object=] specifies the role specified in the Add's [=instrument=]
  1837. - [=allows=] is [=distribute=]
  1838. - [=capability=] is the URI of the delegate-Grant from step 4 or 5 above
  1839. 2. For each delegation Grant *g* that *P* has received from its projects,
  1840. components or parent teams, *P* sends an
  1841. [[#extending-a-delegation-chain|extension-Grant]] where:
  1842. - [=actor=] specifies *P*
  1843. - [=context=] is identical to *g*'s [=context=]
  1844. - [=target=] specifies *C*
  1845. - [=object=] specifies the lower role among the one specified by the
  1846. Add's [=instrument=], and the one specified by *g*'s [=object=]
  1847. - [=allows=] is [=distribute=]
  1848. - [=capability=] is the URI of the delegate-Grant from step 4 or 5 above
  1849. - [=delegates=] specifies *g*'s URI
  1850. When *C* sees these Grants, it delegates them further, as mentioned above.
  1851. In addition, as long as the parent-child link remains active, whenever *P*
  1852. receives a delegation Grant from any of its projects, components or parents, it
  1853. SHOULD send *C* an extension-Grant as described above, and *C* SHOULD extend it
  1854. further.
  1855. ### Removing a team child-parent link
  1856. #### Initiated by the child
  1857. Assuming:
  1858. - Teams *P* and *C*, where *P* is a parent of *C*
  1859. - A person with admin access to *C*, Celia
  1860. Celia wants *C* to stop being a child of *P* (which means *P* will stop
  1861. being a parent of *C*). The exchange of activities SHOULD be as follows:
  1862. 1. Celia sends an [=Remove=] activity in which:
  1863. - [=object=] is *P*
  1864. - [=origin=] is the URI of *C*'s [=context=] (i.e. parents) collection
  1865. - [=capability=] is the URI of a [=Grant=] that authorizes Celia's admin
  1866. access to *C*
  1867. 2. *C*, seeing and authorizing the Remove, publishes a [=Revoke=] activity in
  1868. which the [=object=] is the delegate-Grant it had sent to *P*, and now
  1869. considers that Grant revoked, and removes *P* from its [=context=] (i.e.
  1870. parents) collection
  1871. 3. *P*, seeing *C*'s Revoke, removes *C* from its [=subteams=] collection,
  1872. and MAY publish an Accept (whose [=object=] is the Remove) to notify its
  1873. followers
  1874. Now, for each Grant *g* that *C* has published, as a extension of a Grant it
  1875. had received from *P*:
  1876. 1. *C* sends (to *g*'s [=target=]) a [=Revoke=] activity whose [=object=]
  1877. specifies *g*'s URI
  1878. 2. *C* considers *g* invalidated
  1879. 3. The recipient of the [=Revoke=], if it further published extensions of *g*,
  1880. now similarly publishes Revoke activities and considers those extension
  1881. Grants invalid
  1882. #### Initiated by the parent
  1883. Assuming:
  1884. - Teams *P* and *C*, where *P* is a parent of *C*
  1885. - A person with admin access to *P*, Philip
  1886. Philip wants *C* to stop being a child of *P* (which means *P* will stop
  1887. being a parent of *C*). The exchange of activities SHOULD be as follows:
  1888. 1. Philip sends an [=Remove=] activity in which:
  1889. - [=object=] is *C*
  1890. - [=origin=] is the URI of *P*'s [=subteams=] (i.e. parents) collection
  1891. - [=capability=] is the URI of a [=Grant=] that authorizes Philip's admin
  1892. access to *P*
  1893. 2. *P*, seeing and authorizing the Remove, publishes an [=Accept=] where
  1894. [=object=] is the Add's URI
  1895. 3. *C*, seeing the Remove and the Accept, publishes a [=Revoke=] activity in
  1896. which the [=object=] is the delegate-Grant it had sent to *P*, and now
  1897. considers that Grant revoked, and removes *P* from its [=context=] (i.e.
  1898. parents) collection
  1899. 4. *P*, seeing *C*'s Revoke, removes *C* from its [=subteams=] collection
  1900. Now, for each Grant *g* that *C* has published, as a extension of a Grant it
  1901. had received from *P*:
  1902. 1. *C* sends (to *g*'s [=target=]) a [=Revoke=] activity whose [=object=]
  1903. specifies *g*'s URI
  1904. 2. *C* considers *g* invalidated
  1905. 3. The recipient of the [=Revoke=], if it further published extensions of *g*,
  1906. now similarly publishes Revoke activities and considers those extension
  1907. Grants invalid
  1908. # Actor Interface
  1909. Issue: This section will provide, for each actor type, a summary of the various
  1910. (1) "methods" i.e. activities it can receive as requests for action; (2)
  1911. "events", i.e. activities it sends out; (3) perhaps representation of resources
  1912. that are specific to the actor type. Right now there's a grey zone between
  1913. methods and events, because some activities are methods for the target actor
  1914. but events for any other recipient, and some events are actually sent by other
  1915. actors, which cc the target actor's followers and the target actor delivers via
  1916. inbox-forwarding and not by `Announce`ing (or custom `Forward`ing) those
  1917. activities.
  1918. ## Person ## {#person-iface}
  1919. ## Team ## {#team-iface}
  1920. ## Project ## {#project-iface}
  1921. ## Repository ## {#repo-iface}
  1922. ## TicketTracker ## {#tt-iface}
  1923. ## PatchTracker ## {#pt-iface}
  1924. # Client to Server Interactions
  1925. Issue: This section is about how a human or bot can interact with the system by
  1926. POSTing activities into the outbox of a Person (or Application/Service?) actor,
  1927. and managing notifications. It's less urgent than Server-to-Server. fr33 is
  1928. using C2S in Vervis, and will be gradually working on this part.
  1929. ForgeFed uses Activities for client to server interactions, as described by
  1930. ActivityPub. A client will send objects (eg. a Ticket) wrapped in a Activity
  1931. (eg. Create) to an actor's outbox, and in turn the server will take care of
  1932. delivery.
  1933. ## Follow Activity
  1934. The Follow activity is used to subscribe to the activities of a Repository.
  1935. The client MUST send a Follow activity to the Person's outbox. The server
  1936. in turn delivers the message to the destination inbox.
  1937. ## Push Activity
  1938. The Push activity is used to notify followers when somebody has pushed changes
  1939. to a Repository.
  1940. The client MUST send a Push activity to the Repository's outbox. The server
  1941. in turn delivers the message to the Repository followers.
  1942. # Actor and Resource Representation
  1943. Issue: This section is about representation of objects. It's possible that
  1944. actor representation will move into a separate section, as well as objects
  1945. specific to one actor type. And then this section will describe only
  1946. objects/resources used by multiple actor types, such as comments (person, issue
  1947. tracker, PR tracker) and tickets (used for both issues and PRs).
  1948. ## Comment ## {#Comment}
  1949. To represent a comment, e.g. a comment on a ticket or a merge request, use the
  1950. ActivityPub [=Note=] type.
  1951. Properties:
  1952. <pre class=simpledef>
  1953. [=type=]:
  1954. [=Note=]
  1955. [=attributedTo=]:
  1956. The author of the comment
  1957. [=context=]:
  1958. The topic of the discussion, e.g. a ticket or a merge request. It MUST be
  1959. provided.
  1960. [=inReplyTo=]:
  1961. The entity on which this comment replies. MUST be provided. If the comment
  1962. is made directly on the discussion topic, then [=inReplyTo=] MUST be
  1963. identical to [=context=]. Otherwise, set [=inReplyTo=] to the comment to
  1964. which this comment replies. In that case both comments MUST have an
  1965. identical [=context=].
  1966. [=content=], [=mediaType=], [=source=]:
  1967. The comment text, in rendered form and in source form
  1968. </pre>
  1969. <div class=example>
  1970. <xmp highlight=json-ld>
  1971. {
  1972. "@context": "https://www.w3.org/ns/activitystreams",
  1973. "id": "https://forge.example/luke/comments/rD05r",
  1974. "type": "Note",
  1975. "attributedTo": "https://forge.example/luke",
  1976. "context": "https://dev.example/aviva/game-of-life/merge-requests/19",
  1977. "inReplyTo": "https://dev.example/aviva/comments/E9AGE",
  1978. "mediaType": "text/html",
  1979. "content": "<p>Thank you for the review! I'll submit a correction ASAP</p>",
  1980. "source": {
  1981. "mediaType": "text/markdown; variant=Commonmark",
  1982. "content": "Thank you for the review! I'll submit a correction ASAP"
  1983. },
  1984. "published": "2019-11-06T20:49:05.604488Z"
  1985. }
  1986. </xmp>
  1987. </div>
  1988. ## Team Membership
  1989. Properties:
  1990. <pre class=simpledef>
  1991. [=type=]:
  1992. [=Relationship=]
  1993. [=subject=]:
  1994. A [=Team=]
  1995. [=relationship=]:
  1996. [=hasMember=]
  1997. [=object=]:
  1998. A [=Person=] who is a member of the `Team`
  1999. [=tag=]:
  2000. The role that the member has in the team
  2001. </pre>
  2002. <div class=example>
  2003. <xmp highlight=json-ld>
  2004. {
  2005. "@context": [
  2006. "https://www.w3.org/ns/activitystreams",
  2007. "https://forgefed.org/ns"
  2008. ],
  2009. "id": "https://dev.example/teams/mobilizon-dev-team/members/ThmsicTj",
  2010. "type": "Relationship",
  2011. "subject": "https://dev.example/teams/mobilizon-dev-team",
  2012. "relationship": "hasMember",
  2013. "object": "https://dev.example/people/celine",
  2014. "tag": "develop"
  2015. }
  2016. </xmp>
  2017. </div>
  2018. ## Team
  2019. Properties:
  2020. <pre class=simpledef>
  2021. [=type=]:
  2022. [=Team=]
  2023. [=name=]:
  2024. The user-given name of the team, e.g. "Gitea Development Team"
  2025. [=published=]:
  2026. The time the team was created on the server
  2027. [=summary=]:
  2028. A one-line user provided description of the project, as HTML, e.g.
  2029. `"We are creating a code hosting platform"`
  2030. [=members=]:
  2031. [=Collection=] of the members of this team
  2032. [=subteams=]:
  2033. Subteams of this team, i.e. teams whose members (and subteams) inherit the
  2034. access that this team has been granted (to projects, repositories, etc.)
  2035. [=context=]:
  2036. Parent [=Team=]s of this team, i.e. teams from which this team inherits
  2037. access to projects, components and resources, e.g. repositories, ticket
  2038. trackers (and passes them to its [=members=] and inherits them to its own
  2039. [=subteams=])
  2040. </pre>
  2041. <div class=example>
  2042. <xmp highlight=json-ld>
  2043. {
  2044. "@context": [
  2045. "https://www.w3.org/ns/activitystreams",
  2046. "https://w3id.org/security/v2",
  2047. "https://forgefed.org/ns"
  2048. ],
  2049. "id": "https://dev.example/teams/mobilizon-dev-team",
  2050. "type": "Team",
  2051. "name": "Mobilizon Development Team",
  2052. "summary": "We're creating a federated tool for organizing events!",
  2053. "members": {
  2054. "type": "Collection",
  2055. "totalItems": 3,
  2056. "items": [
  2057. { "type": "Relationship",
  2058. "subject": "https://dev.example/teams/mobilizon-dev-team",
  2059. "relationship": "hasMember",
  2060. "object": "https://dev.example/people/alice",
  2061. "tag": "admin"
  2062. },
  2063. { "type": "Relationship",
  2064. "subject": "https://dev.example/teams/mobilizon-dev-team",
  2065. "relationship": "hasMember",
  2066. "object": "https://dev.example/people/bob",
  2067. "tag": "maintain"
  2068. },
  2069. { "type": "Relationship",
  2070. "subject": "https://dev.example/teams/mobilizon-dev-team",
  2071. "relationship": "hasMember",
  2072. "object": "https://dev.example/people/celine",
  2073. "tag": "develop"
  2074. }
  2075. ]
  2076. },
  2077. "subteams": {
  2078. "type": "Collection",
  2079. "totalItems": 2,
  2080. "items": [
  2081. "https://dev.example/teams/mobilizon-backend-team",
  2082. "https://dev.example/teams/mobilizon-frontend-team"
  2083. ]
  2084. },
  2085. "context": "https://dev.example/teams/framasoft-developers",
  2086. "publicKey": {
  2087. "id": "https://dev.example/teams/mobilizon-dev-team#main-key",
  2088. "owner": "https://dev.example/teams/mobilizon-dev-team",
  2089. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  2090. },
  2091. "inbox": "https://dev.example/teams/mobilizon-dev-team/inbox",
  2092. "outbox": "https://dev.example/teams/mobilizon-dev-team/outbox",
  2093. "followers": "https://dev.example/teams/mobilizon-dev-team/followers"
  2094. }
  2095. </xmp>
  2096. </div>
  2097. ## Project ## {#Project}
  2098. Properties:
  2099. <pre class=simpledef>
  2100. [=type=]:
  2101. [=Project=]
  2102. [=name=]:
  2103. The user-given name of the project, e.g. "My cool project"
  2104. [=published=]:
  2105. The time the project was created on the server
  2106. [=summary=]:
  2107. A one-line user provided description of the project, as HTML, e.g.
  2108. "`<p>A command-line tool that does cool things</p>`"
  2109. [=ticketsTrackedBy=]:
  2110. The default ticket tracker to use when submitting a ticket to this project
  2111. (this tracker MUST be listed under the project's [=components=])
  2112. [=subprojects=]:
  2113. A [=Collection=] of the subprojects of this project
  2114. [=context=]:
  2115. The parent [=Project=](s) to which this project belongs
  2116. </pre>
  2117. <div class=example>
  2118. <xmp highlight=json-ld>
  2119. {
  2120. "@context": [
  2121. "https://www.w3.org/ns/activitystreams",
  2122. "https://forgefed.org/ns"
  2123. ],
  2124. "id": "https://dev.example/projects/wanderer",
  2125. "type": "Project",
  2126. "name": "Wanderer",
  2127. "summary": "3D nature exploration game",
  2128. "components": {
  2129. "type": "Collection",
  2130. "totalItems": 7,
  2131. "items": [
  2132. "https://dev.example/repos/opengl-vegetation",
  2133. "https://dev.example/repos/opengl-vegetation/patch-tracker",
  2134. "https://dev.example/repos/treesim",
  2135. "https://dev.example/repos/treesim/patch-tracker",
  2136. "https://dev.example/repos/wanderer",
  2137. "https://dev.example/repos/wanderer/patch-tracker",
  2138. "https://dev.example/issue-trackers/wanderer"
  2139. ]
  2140. },
  2141. "subprojects": {
  2142. "type": "Collection",
  2143. "totalItems": 2,
  2144. "items": [
  2145. "https://dev.example/projects/nature-3d-models",
  2146. "https://dev.example/projects/wanderer-fundraising"
  2147. ]
  2148. },
  2149. "ticketsTrackedBy": "https://dev.example/issue-trackers/wanderer",
  2150. "inbox": "https://dev.example/projects/wanderer/inbox",
  2151. "outbox": "https://dev.example/projects/wanderer/outbox",
  2152. "followers": "https://dev.example/projects/wanderer/followers"
  2153. }
  2154. </xmp>
  2155. </div>
  2156. ## Commit
  2157. To represent a named set of changes committed into a repository's history, use
  2158. the ForgeFed [=Commit=] type. Such a committed change set is called
  2159. e.g. a *commit* in Git, and a *patch* in Darcs.
  2160. Properties:
  2161. <pre class=simpledef>
  2162. [=type=]:
  2163. [=Commit=]
  2164. [=context=]:
  2165. The [=Repository=] that this commit belongs to
  2166. [=attributedTo=]:
  2167. The commit author; if their actor URI is unknown, it MAY be their email
  2168. address as a `mailto` URI
  2169. [=created=]:
  2170. A value of type [=dateTime=] (i.e. an ISO 8601 datetime value) specifying
  2171. the time at which the commit was written by its author
  2172. [=committedBy=]:
  2173. The entity that committed the commit's changes into their local copy of the
  2174. repo, before the commit was pushed; if their actor URI is unknown, it MAY
  2175. be their email address as a `mailto` URI
  2176. [=committed=]:
  2177. The time the commit was committed by its committer
  2178. [=hash=]:
  2179. The hash identifying the commit, e.g. the commit SHA1 hash in Git; the
  2180. patch info SHA1 hash in Darcs
  2181. [=summary=]:
  2182. The commit's one-line title as HTML-escaped plain text; if the commit title
  2183. and description are a single commit message string, then the title is the
  2184. 1st line of the commit message
  2185. [=description=]:
  2186. A JSON object with a [=mediaType=] field and a [=content=] field, where
  2187. `mediaType` SHOULD be "text/plain" and `content` is the commit's
  2188. possibly-multi-line description; if the commit title and description are a
  2189. single commit message string, then the description is everything after the
  2190. 1st line of the commit message (possibly with leading whitespace stripped)
  2191. </pre>
  2192. <div class=example>
  2193. <xmp highlight=json-ld>
  2194. {
  2195. "@context": [
  2196. "https://www.w3.org/ns/activitystreams",
  2197. "https://forgefed.org/ns"
  2198. ],
  2199. "id": "https://example.dev/alice/myrepo/commits/109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
  2200. "type": "Commit",
  2201. "context": "https://example.dev/alice/myrepo",
  2202. "attributedTo": "https://example.dev/bob",
  2203. "created": "2019-07-11T12:34:56Z",
  2204. "committedBy": "https://example.dev/alice",
  2205. "committed": "2019-07-26T23:45:01Z",
  2206. "hash": "109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
  2207. "summary": "Add an installation script, fixes issue #89",
  2208. "description": {
  2209. "mediaType": "text/plain",
  2210. "content": "It's about time people can install it on their computers!"
  2211. }
  2212. }
  2213. </xmp>
  2214. </div>
  2215. ## Branch
  2216. To represent a repository branch, use the ForgeFed [=Branch=] type.
  2217. It can be a real built-in version control system branch (such as a Git branch)
  2218. or a copy of the repo used as a branch (e.g. in Darcs, which doesn't implement
  2219. branches, and the way to have branches is to keep multiple versions of the
  2220. repo).
  2221. Properties:
  2222. <pre class=simpledef>
  2223. [=type=]:
  2224. [=Branch=]
  2225. [=context=]:
  2226. The [=Repository=] that this branch belongs to
  2227. [=name=]:
  2228. The user given name of the branch, e.g. "main"
  2229. [=ref=]:
  2230. The unique identifier of the branch within the repo, e.g. "refs/heads/main"
  2231. [=team=]:
  2232. If the branch has its own access/authority/visibility settings, this can be
  2233. a [=Collection=] of the actors who have push/edit access to the branch
  2234. </pre>
  2235. <div class=example>
  2236. <xmp highlight=json-ld>
  2237. {
  2238. "@context": [
  2239. "https://www.w3.org/ns/activitystreams",
  2240. "https://forgefed.org/ns"
  2241. ],
  2242. "id": "https://example.dev/luke/myrepo/branches/master",
  2243. "type": "Branch",
  2244. "context": "https://example.dev/luke/myrepo",
  2245. "name": "master",
  2246. "ref": "refs/heads/master"
  2247. }
  2248. </xmp>
  2249. </div>
  2250. ## Repository ## {#Repository}
  2251. To represent a version control repository, use the ForgeFed
  2252. [=Repository=] type.
  2253. Properties:
  2254. <pre class=simpledef>
  2255. [=type=]:
  2256. [=Repository=]
  2257. [=name=]:
  2258. The user given name of the repository, e.g. "My cool repo"
  2259. [=cloneUri=]:
  2260. The endpoint from which the content of the repository can be obtained via
  2261. the native protocol (Git, Hg, etc.)
  2262. [=attributedTo=]:
  2263. The actor(s) in charge of the repository, e.g. a person or an organization;
  2264. if their actor URI is unknown, it MAY be their email address as a `mailto`
  2265. URI
  2266. [=published=]:
  2267. The time the repository was created on the server
  2268. [=summary=]:
  2269. A one-line user provided description of the repository, as HTML, e.g.
  2270. "`<p>A command-line tool that does cool things</p>`"
  2271. [=team=]:
  2272. [=Collection=] of actors who have management/push access to the repository,
  2273. or the subset of them who is available and wants to be
  2274. contacted/notified/responsible on repo access related activities/requests
  2275. [=forks=]:
  2276. [=OrderedCollection=] of repositories that are forks of this repository
  2277. [=ticketsTrackedBy=]:
  2278. The ticket tracker that tracks tickets for this repository, this can be the
  2279. repository itself if it manages its own tickets
  2280. [=sendPatchesTo=]:
  2281. The actor that tracks patches for this repository, this can be the
  2282. repository itself if it manages its own patches and merge requests. For
  2283. example it may be some external tracker or service, or the user or team to
  2284. whom the repository belongs.
  2285. [=context=]:
  2286. The [=Project=](s) to which this repository belongs
  2287. </pre>
  2288. <div class=example>
  2289. <xmp highlight=json-ld>
  2290. {
  2291. "@context": [
  2292. "https://www.w3.org/ns/activitystreams",
  2293. "https://w3id.org/security/v1",
  2294. "https://forgefed.org/ns"
  2295. ],
  2296. "id": "https://dev.example/aviva/treesim",
  2297. "cloneUri": "https://dev.example/aviva/treesim.git",
  2298. "type": "Repository",
  2299. "publicKey": {
  2300. "id": "https://dev.example/aviva/treesim#main-key",
  2301. "owner": "https://dev.example/aviva/treesim",
  2302. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  2303. },
  2304. "inbox": "https://dev.example/aviva/treesim/inbox",
  2305. "outbox": "https://dev.example/aviva/treesim/outbox",
  2306. "followers": "https://dev.example/aviva/treesim/followers",
  2307. "team": "https://dev.example/aviva/treesim/team",
  2308. "ticketsTrackedBy": "https://dev.example/aviva/treesim",
  2309. "sendPatchesTo": "https://dev.example/aviva/treesim",
  2310. "name": "Tree Growth 3D Simulation",
  2311. "attributedTo": "https://example.dev/bob",
  2312. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>"
  2313. }
  2314. </xmp>
  2315. </div>
  2316. ## Push ## {#Push}
  2317. To represent an event of [=Commit=]s being pushed to a
  2318. [=Repository=], use a ForgeFed [=Push=] activity.
  2319. Properties:
  2320. <pre class=simpledef>
  2321. [=type=]:
  2322. [=Push=]
  2323. [=actor=]:
  2324. The [=Repository=] to which the push was made, and that is publishing this
  2325. Push activity
  2326. [=attributedTo=]:
  2327. The entity (person, bot, etc.) that pushed the commits
  2328. [=target=]:
  2329. The specific repo history tip onto which the commits were added, this is
  2330. either a [=Branch=] (for VCSs that have branches) or a [=Repository=] (for
  2331. VCSs that don't have branches, only a single history line). If it's a
  2332. branch, it MUST be a branch belonging to the repository specified by
  2333. [=actor=]. And if it's a repository, it MUST be identical to the one
  2334. specified by [=actor=].
  2335. [=hashBefore=]:
  2336. Repo/branch/tip hash before adding the new commits
  2337. [=hashAfter=]:
  2338. Repo/branch/tip hash after adding the new commits
  2339. [=object=]:
  2340. An [=OrderedCollection=] of the [=Commit=]s being pushed, in **reverse
  2341. chronological order**. The [=items=] (or [=orderedItems=]) property of the
  2342. collection MUST contain either the whole list of commits being pushed, or a
  2343. prefix i.e. continuous subset from the beginning of the list (therefore the
  2344. **latest** commits). [=earlyItems=] MAY be used for listing a suffix i.e.
  2345. continuous subset from the end (therefore the **earliest** commits).
  2346. </pre>
  2347. <div class=example>
  2348. <xmp highlight=json-ld>
  2349. {
  2350. "@context": [
  2351. "https://www.w3.org/ns/activitystreams",
  2352. "https://forgefed.org/ns"
  2353. ],
  2354. "id": "https://dev.example/aviva/outbox/E26bE",
  2355. "type": "Push",
  2356. "actor": "https://dev.example/aviva",
  2357. "to": [
  2358. "https://dev.example/aviva/followers",
  2359. "https://dev.example/aviva/game-of-life",
  2360. "https://dev.example/aviva/game-of-life/team",
  2361. "https://dev.example/aviva/game-of-life/followers"
  2362. ],
  2363. "context": "https://dev.example/aviva/game-of-life",
  2364. "target": "https://dev.example/aviva/game-of-life/branches/master",
  2365. "hashBefore": "017cbb00bc20d1cae85f46d638684898d095f0ae",
  2366. "hashAfter": "be9f48a341c4bb5cd79ae7ab85fbf0c05d2837bb",
  2367. "object": {
  2368. "totalItems": 2,
  2369. "type": "OrderedCollection",
  2370. "orderedItems": [
  2371. {
  2372. "id": "https://dev.example/aviva/game-of-life/commits/be9f48a341c4bb5cd79ae7ab85fbf0c05d2837bb",
  2373. "type": "Commit",
  2374. "attributedTo": "https://dev.example/aviva",
  2375. "context": "https://dev.example/aviva/game-of-life",
  2376. "hash": "be9f48a341c4bb5cd79ae7ab85fbf0c05d2837bb",
  2377. "created": "2019-12-02T16:07:32Z",
  2378. "summary": "Add widget to alter simulation speed"
  2379. },
  2380. {
  2381. "id": "https://dev.example/aviva/game-of-life/commits/fa37fe100a8b1e69933889c5bf3caf95cd3ae1e6",
  2382. "type": "Commit",
  2383. "attributedTo": "https://dev.example/aviva",
  2384. "context": "https://dev.example/aviva/game-of-life",
  2385. "hash": "fa37fe100a8b1e69933889c5bf3caf95cd3ae1e6",
  2386. "created": "2019-12-02T15:51:52Z",
  2387. "summary": "Set window title correctly, fixes issue #7"
  2388. }
  2389. ]
  2390. }
  2391. }
  2392. </xmp>
  2393. </div>
  2394. ## Ticket ## {#Ticket}
  2395. To represent a work item in a project, use the ForgeFed [=Ticket=]
  2396. type.
  2397. TODO decide on ticket categories/subtypes and update below
  2398. TODO decide on property for titles, update below
  2399. TODO properly document `history` or remove it from example
  2400. Properties:
  2401. <pre class=simpledef>
  2402. [=type=]:
  2403. [=Ticket=]
  2404. [=context=]:
  2405. The [=TicketTracker=] or [=PatchTracker=] to which this ticket belongs
  2406. [=attributedTo=]:
  2407. The actor (person, bot, etc.) who submitted the ticket
  2408. [=summary=]:
  2409. The ticket's one-line title, as HTML-escaped plain text
  2410. [=content=], [=mediaType=]:
  2411. The ticket's (possibly multi-line) detailed description text, in rendered
  2412. form
  2413. [=source=]:
  2414. Source form of the ticket's description
  2415. [=published=]:
  2416. The time the ticket submission was accepted (which may not be the same as
  2417. the time the ticket was submitted)
  2418. [=followers=]:
  2419. Collection of the followers of the ticket, actors who want to be notified
  2420. on activity related to the ticket
  2421. [=team=]:
  2422. Collection of project team members who have responsibility for work on this
  2423. ticket and want to be notified on activities related to it
  2424. [=replies=]:
  2425. Collection of direct comments made on the ticket (but not comments made *on
  2426. other* comments on the ticket)
  2427. [=dependants=]:
  2428. Collection of [=Ticket=]s which depend on this ticket
  2429. [=dependencies=]:
  2430. Collection of [=Ticket=]s on which this ticket depends
  2431. [=isResolved=]:
  2432. Whether the work on this ticket is done
  2433. [=resolvedBy=]:
  2434. If the work on this ticket is done, who marked the ticket as resolved, or
  2435. which activity did so
  2436. [=resolved=]:
  2437. When the ticket has been marked as resolved
  2438. </pre>
  2439. There's an important distinction between these two kinds of tickets:
  2440. - Task:
  2441. A work item which tracks some task to be done, in which the task and its
  2442. results are described in text, but the work itself is done elsewhere. This
  2443. is often called "issue" in software project hosting platforms. Tasks are
  2444. general-purpose work items that aren't specific to software development or
  2445. software projects.
  2446. - Merge request:
  2447. A work item that includes a proposal or request to apply some specific
  2448. changes to a specific [=Repository=]. The work requested by the work item
  2449. is to review and (decide whether to) merge the proposed patches to the
  2450. repository. This kind of work item is often called "Pull Request" or "Merge
  2451. Request" on software project hosting platforms.
  2452. ### Task / Issue
  2453. A task is represented as a [[#Ticket]] as described above, with the
  2454. following additional requirements:
  2455. - [=context=] is the [=TicketTracker=] to which this task belongs
  2456. - There is no [=attachment=] of type [=Offer=] (but there may be attachment of other types)
  2457. <div class=example>
  2458. <xmp highlight=json-ld>
  2459. {
  2460. "@context": [
  2461. "https://www.w3.org/ns/activitystreams",
  2462. "https://forgefed.org/ns"
  2463. ],
  2464. "id": "https://dev.example/aviva/game-of-life/issues/107",
  2465. "type": "Ticket",
  2466. "context": "https://dev.example/aviva/game-of-life",
  2467. "attributedTo": "https://forge.example/luke",
  2468. "summary": "Window title is empty",
  2469. "content": "<p>When I start the simulation, window title disappears suddenly</p>",
  2470. "mediaType": "text/html",
  2471. "source": {
  2472. "mediaType": "text/markdown; variant=Commonmark",
  2473. "content": "When I start the simulation, window title disappears suddenly",
  2474. },
  2475. "published": "2019-11-04T07:00:04.465807Z",
  2476. "followers": "https://dev.example/aviva/game-of-life/issues/107/followers",
  2477. "team": "https://dev.example/aviva/game-of-life/issues/107/team",
  2478. "replies": "https://dev.example/aviva/game-of-life/issues/107/discussion",
  2479. "history": "https://dev.example/aviva/game-of-life/issues/107/activity",
  2480. "dependants": "https://dev.example/aviva/game-of-life/issues/107/rdeps",
  2481. "dependencies": "https://dev.example/aviva/game-of-life/issues/107/deps",
  2482. "isResolved": true,
  2483. "resolvedBy": "https://code.example/martin",
  2484. "resolved": "2020-02-07T06:45:03.281314Z"
  2485. }
  2486. </xmp>
  2487. </div>
  2488. ### Merge Request / Pull Request
  2489. A merge request is represented as a [[#Ticket]] as described above, with
  2490. the following additional requirements:
  2491. - [=context=] is the [=PatchTracker=] to which this merge request belongs
  2492. - There is no [=attachment=] of type [=Offer=] (but there may be attachment of other types)
  2493. - There is exactly one [=attachment=] of type [=Offer=], as described below
  2494. - There MAY be more [=attachment=]s, but they MUST NOT be of type [=Offer=]
  2495. In that special [=attachment=] of type [=Offer=]:
  2496. - [=type=] is [=Offer=]
  2497. - [=origin=] is the [=Repository=] or [=Branch=] from which the proposed changes are proposed to be merged into the target repository/branch
  2498. - [=target=] is the [=Repository=] or [=Branch=] into which the changes are proposed to be merged
  2499. - [=object=] is an [=OrderedCollection=] of [=Patch=]es in reverse
  2500. chronological order, in which, in addition to standard [=OrderedCollection=]
  2501. properties:
  2502. - [=context=] is (the [=id=] of) the [[#Ticket]]
  2503. - [=previousVersions=] is a list of previous versions
  2504. of the merge request's proposed changes, i.e. previous versions of this
  2505. [=OrderedCollection=]; each of those uses
  2506. [=currentVersion=] to point back to this latest
  2507. version
  2508. <div class=example>
  2509. <xmp highlight=json-ld>
  2510. {
  2511. "@context": [
  2512. "https://www.w3.org/ns/activitystreams",
  2513. "https://forgefed.org/ns"
  2514. ],
  2515. "id": "https://dev.example/aviva/game-of-life/pulls/825",
  2516. "type": "Ticket",
  2517. "context": "https://dev.example/aviva/game-of-life",
  2518. "attributedTo": "https://forge.example/luke",
  2519. "summary": "Fix the empty window title bug",
  2520. "content": "<p>This fixes the bug making the title disappear</p>",
  2521. "mediaType": "text/html",
  2522. "source": {
  2523. "mediaType": "text/markdown; variant=Commonmark",
  2524. "content": "This fixes the bug making the title disappear",
  2525. },
  2526. "published": "2022-09-15T14:52:00.125987Z",
  2527. "followers": "https://dev.example/aviva/game-of-life/pulls/825/followers",
  2528. "replies": "https://dev.example/aviva/game-of-life/pulls/825/discussion",
  2529. "isResolved": false,
  2530. "attachment": {
  2531. "type": "Offer",
  2532. "origin": {
  2533. "type": "Branch",
  2534. "context": "https://forge.example/luke/game-of-life",
  2535. "ref": "refs/heads/fix-title-bug"
  2536. },
  2537. "target": {
  2538. "type": "Branch",
  2539. "context": "https://dev.example/aviva/game-of-life",
  2540. "ref": "refs/heads/main"
  2541. },
  2542. "object": {
  2543. "id": "https://dev.example/aviva/game-of-life/pulls/825/versions/1",
  2544. "type": "OrderedCollection",
  2545. "totalItems": 1,
  2546. "items": [
  2547. {
  2548. "type": "Patch",
  2549. "attributedTo": "https://forge.example/luke",
  2550. "context": "https://dev.example/aviva/game-of-life/pulls/825/versions/1",
  2551. "mediaType": "application/x-git-patch",
  2552. "content": "From c9ae5f4ff4a330b6e1196ceb7db1665bd4c1..."
  2553. }
  2554. ],
  2555. "context": "https://dev.example/aviva/game-of-life/pulls/825"
  2556. }
  2557. }
  2558. }
  2559. </xmp>
  2560. </div>
  2561. ## Patch
  2562. <pre class=simpledef>
  2563. [=type=]:
  2564. [=Patch=]
  2565. [=attributedTo=]:
  2566. The [=Person=] who has written the patch
  2567. [=context=]:
  2568. An [=OrderedCollection=] representing a sequence of patches, being
  2569. submitted together as a proposed change to a certain [=Repository=], and
  2570. this patch is an item in that collection
  2571. [=content=]:
  2572. A description of the changes that this patch proposes, encoded in the
  2573. format specified by [=mediaType=]
  2574. [=mediaType=]:
  2575. A native patch format used by the Version Control System of the
  2576. [=Repository=] to which the patch is proposed, and in which the [=content=]
  2577. of this patch is encoded
  2578. </pre>
  2579. <div class=example>
  2580. <xmp highlight=json-ld>
  2581. {
  2582. "@context": [
  2583. "https://www.w3.org/ns/activitystreams",
  2584. "https://forgefed.org/ns"
  2585. ],
  2586. "id": "https://dev.example/aviva/game-of-life/pulls/825/versions/1/patches/1",
  2587. "type": "Patch",
  2588. "attributedTo": "https://forge.example/luke",
  2589. "context": "https://dev.example/aviva/game-of-life/pulls/825/versions/1",
  2590. "mediaType": "application/x-git-patch",
  2591. "content": "From c9ae5f4ff4a330b6e1196ceb7db1665bd4c1..."
  2592. }
  2593. </xmp>
  2594. </div>
  2595. # Access Control
  2596. ## Giving Access
  2597. ### Invite ### {#Invite}
  2598. To offer some actor access to a shared resource (such as a repository or a
  2599. ticket tracker), use an ActivityPub [=Invite=] activity.
  2600. Properties:
  2601. <pre class=simpledef>
  2602. [=type=]:
  2603. [=Invite=]
  2604. [=actor=]:
  2605. The entity (person, bot, etc.) that is offering access
  2606. [=instrument=]:
  2607. A [=Role=] specifying which operations on the resource are being allowed
  2608. [=target=]:
  2609. The resource, access to which is being given (for example, a repository)
  2610. [=object=]:
  2611. The actor who is being gives access to the resource
  2612. [=capability=]:
  2613. A previously published `Grant`, giving the `actor` permission to invite
  2614. more actors to access the resource
  2615. </pre>
  2616. <div class=example>
  2617. <xmp highlight=json-ld>
  2618. {
  2619. "@context": [
  2620. "https://www.w3.org/ns/activitystreams",
  2621. "https://forgefed.org/ns"
  2622. ],
  2623. "id": "https://dev.example/aviva/outbox/B47d3",
  2624. "type": "Invite",
  2625. "actor": "https://dev.example/aviva",
  2626. "to": [
  2627. "https://dev.example/aviva/followers",
  2628. "https://coding.community/repos/game-of-life",
  2629. "https://coding.community/repos/game-of-life/followers",
  2630. "https://software.site/bob",
  2631. "https://software.site/bob/followers"
  2632. ],
  2633. "instrument": "maintain",
  2634. "target": "https://coding.community/repos/game-of-life",
  2635. "object": "https://software.site/bob",
  2636. "capability": "https://coding.community/repos/game-of-life/outbox/2c53A"
  2637. }
  2638. </xmp>
  2639. </div>
  2640. ### Join ### {#Join}
  2641. To request access to a shared resource, use an ActivityPub [=Join=] activity.
  2642. Properties:
  2643. <pre class=simpledef>
  2644. [=type=]:
  2645. [=Join=]
  2646. [=actor=]:
  2647. The entity (person, bot, etc.) that is requesting access
  2648. [=instrument=]:
  2649. A [=Role=] specifying which operations on the resource are being requested
  2650. [=object=]:
  2651. The resource, access to which is being given (for example, a repository)
  2652. [=capability=]:
  2653. *(optional)* A previously published `Grant`, giving the `actor` permission
  2654. to gain access to the resource without the approval of another actor. If
  2655. `capability` isn't provided, the resource won't grant access before someone
  2656. with adequate access approves the Join request.
  2657. </pre>
  2658. <div class=example>
  2659. <xmp highlight=json-ld>
  2660. {
  2661. "@context": [
  2662. "https://www.w3.org/ns/activitystreams",
  2663. "https://forgefed.org/ns"
  2664. ],
  2665. "id": "https://software.site/bob/outbox/c97E3",
  2666. "type": "Join",
  2667. "actor": "https://software.site/bob",
  2668. "to": [
  2669. "https://coding.community/repos/game-of-life",
  2670. "https://coding.community/repos/game-of-life/followers",
  2671. "https://software.site/bob/followers"
  2672. ],
  2673. "instrument": "maintain",
  2674. "object": "https://coding.community/repos/game-of-life",
  2675. "capability": "https://coding.community/repos/game-of-life/outbox/d38Fa"
  2676. }
  2677. </xmp>
  2678. </div>
  2679. ### Grant ### {#Grant}
  2680. To give some actor access to a shared resource, use a ForgeFed
  2681. [=Grant=] activity.
  2682. Properties:
  2683. <pre class=simpledef>
  2684. [=type=]:
  2685. [=Grant=]
  2686. [=actor=]:
  2687. The entity (person, bot, etc.) that is giving access
  2688. [=object=]:
  2689. A [=Role=] specifying which operations on the resource are being allowed
  2690. [=context=]:
  2691. The resource, access to which is being given (for example, a repository)
  2692. [=target=]:
  2693. The actor who is being gives access to the resource
  2694. [=fulfills=]:
  2695. The activity that triggered the sending of the `Grant`, such as a related
  2696. `Invite` (another example&#x3a; if Alice [=Create=]s a new repository, the
  2697. repository may automatically send back a [=Grant=] giving Alice admin
  2698. access, and this Grant's `fulfills` refers to the [=Create=] that Alice
  2699. sent)
  2700. [=result=]:
  2701. A URI that can be used later for verifying that the given access is still
  2702. approved, thus allowing the actor granting the access to revoke it.
  2703. Alternatively, a JSON object where [=id=] is the URI and [=duration=] MAY
  2704. be specified to allow to skip the revocation check if the duration time
  2705. hasn't yet passed since the last check. If [=duration=] is specified, it
  2706. MUST be positive, and specify only an integral number of seconds that is
  2707. less than `2^63`, and no other component.
  2708. [=allows=]:
  2709. Modes of invocation and/or delegation that this `Grant` is meant to be used
  2710. for
  2711. [=delegates=]:
  2712. If this `Grant` is a delegation, i.e. it is passing on some access that it
  2713. has received, `delegates` specifies the parent `Grant` that it has received
  2714. and now passing on
  2715. [=startTime=]:
  2716. *(optional)* The time at which the Grant becomes valid
  2717. [=endTime=]:
  2718. *(recommended)* The time at which the Grant expires
  2719. </pre>
  2720. <div class=example>
  2721. <xmp highlight=json-ld>
  2722. {
  2723. "@context": [
  2724. "https://www.w3.org/ns/activitystreams",
  2725. "https://forgefed.org/ns"
  2726. ],
  2727. "id": "https://coding.community/repos/game-of-life/outbox/9fA8c",
  2728. "type": "Grant",
  2729. "actor": "https://coding.community/repos/game-of-life",
  2730. "to": [
  2731. "https://dev.example/aviva",
  2732. "https://dev.example/aviva/followers",
  2733. "https://coding.community/repos/game-of-life/followers",
  2734. "https://software.site/bob",
  2735. "https://software.site/bob/followers"
  2736. ],
  2737. "object": "maintain",
  2738. "context": "https://coding.community/repos/game-of-life",
  2739. "target": "https://software.site/bob",
  2740. "fulfills": "https://dev.example/aviva/outbox/B47d3",
  2741. "allows": "invoke",
  2742. "endTime": "2023-12-31T23:00:00-08:00"
  2743. }
  2744. </xmp>
  2745. </div>
  2746. ## Canceling Access
  2747. ### Remove ### {#Remove}
  2748. To disable an actor's membership in a shared resource, invalidating their
  2749. access to it, use an ActivityPub [=Remove=] activity.
  2750. Properties:
  2751. <pre class=simpledef>
  2752. [=type=]:
  2753. [=Remove=]
  2754. [=actor=]:
  2755. The actor (person, bot, etc.) that is disabling access disabled
  2756. [=object=]:
  2757. The actor whose access to the resource is being taken away
  2758. [=origin=]:
  2759. The resource, access to which is being taken away (for example, a
  2760. repository)
  2761. [=capability=]:
  2762. A previously published `Grant`, giving the `actor` permission to disable
  2763. the [=object=] actor's access to the resource
  2764. </pre>
  2765. <div class=example>
  2766. <xmp highlight=json-ld>
  2767. {
  2768. "@context": [
  2769. "https://www.w3.org/ns/activitystreams",
  2770. "https://forgefed.org/ns"
  2771. ],
  2772. "id": "https://dev.example/aviva/outbox/F941b",
  2773. "type": "Remove",
  2774. "actor": "https://dev.example/aviva",
  2775. "to": [
  2776. "https://dev.example/aviva/followers",
  2777. "https://coding.community/repos/game-of-life",
  2778. "https://coding.community/repos/game-of-life/followers",
  2779. "https://software.site/bob",
  2780. "https://software.site/bob/followers"
  2781. ],
  2782. "origin": "https://coding.community/repos/game-of-life",
  2783. "object": "https://software.site/bob",
  2784. "capability": "https://coding.community/repos/game-of-life/outbox/2c53A"
  2785. }
  2786. </xmp>
  2787. </div>
  2788. ### Leave ### {#Leave}
  2789. To withdraw your consent for membership in a shared resource, invalidating
  2790. your access to it, use an ActivityPub [=Leave=] activity.
  2791. Properties:
  2792. <pre class=simpledef>
  2793. [=type=]:
  2794. [=Leave=]
  2795. [=actor=]:
  2796. The actor (person, bot, etc.) that is requesting to disable their own
  2797. access
  2798. [=object=]:
  2799. The resource, access to which is being disabled (for example, a repository)
  2800. </pre>
  2801. <div class=example>
  2802. <xmp highlight=json-ld>
  2803. {
  2804. "@context": [
  2805. "https://www.w3.org/ns/activitystreams",
  2806. "https://forgefed.org/ns"
  2807. ],
  2808. "id": "https://software.site/bob/outbox/d08F4",
  2809. "type": "Leave",
  2810. "actor": "https://software.site/bob",
  2811. "to": [
  2812. "https://coding.community/repos/game-of-life",
  2813. "https://coding.community/repos/game-of-life/followers",
  2814. "https://software.site/bob/followers"
  2815. ],
  2816. "object": "https://coding.community/repos/game-of-life"
  2817. }
  2818. </xmp>
  2819. </div>
  2820. ### Revoke ### {#Revoke}
  2821. Another activity that can be used for disabling access is [=Revoke=].
  2822. While [[#Remove]] and [[#Leave]] are meant for undoing the effects
  2823. of [[#Invite]] and [[#Join]], `Revoke` is provided as an opposite of
  2824. [[#Grant]]. See the Behavior specification for more information about the
  2825. usage of these different activity types in revocation of access to shared
  2826. resources.
  2827. Properties:
  2828. <pre class=simpledef>
  2829. [=type=]:
  2830. [=Revoke=]
  2831. [=actor=]:
  2832. The actor (person, bot, etc.) that is revoking access
  2833. [=fulfills=]:
  2834. An activity that triggered the sending of the `Revoke`, such as a related
  2835. `Remove` or `Leave`
  2836. [=object=]:
  2837. specific [[#Grant]] activities being undone, i.e. the access that they
  2838. granted is now disabled and it cannot be used anymore as the [=capability=]
  2839. of activities. Each Grant may either be mentioned by its [=id=] URI, or be
  2840. included as a full object that includes an [[fep-8b32|integrity proof]].
  2841. </pre>
  2842. <div class=example>
  2843. <xmp highlight=json-ld>
  2844. {
  2845. "@context": [
  2846. "https://www.w3.org/ns/activitystreams",
  2847. "https://forgefed.org/ns"
  2848. ],
  2849. "id": "https://coding.community/repos/game-of-life/outbox/1C0e2",
  2850. "type": "Revoke",
  2851. "actor": "https://coding.community/repos/game-of-life",
  2852. "to": [
  2853. "https://coding.community/repos/game-of-life/followers",
  2854. "https://software.site/bob",
  2855. "https://software.site/bob/followers"
  2856. ],
  2857. "object": "https://coding.community/repos/game-of-life/outbox/9fA8c"
  2858. }
  2859. </xmp>
  2860. </div>
  2861. ### Undo a Grant ### {#undo-grant}
  2862. The Behavior spec describes flows in which the [[#Revoke]] activity is
  2863. used by resources (more accurately, by the actors managing them) to announce
  2864. that they're disabling [[#Grant]]s that they previously sent. To allow for
  2865. a clear distinction, another activity is provided here, for *other* actors to
  2866. *request* the revocation of specific [[#Grant]]s: The ActivityPub [=Undo=]
  2867. activity.
  2868. It's likely that `Grant`s would exist behind-the-scenes in applications, and
  2869. human actors would then use activities such as `Remove` and `Leave` for
  2870. disabling access. But the ability to disable specific `Grant`s may be required
  2871. for ensuring and maintaining system security, therefore `Undo` is provided here
  2872. as well.
  2873. Properties:
  2874. <pre class=simpledef>
  2875. [=type=]:
  2876. [=Undo=]
  2877. [=actor=]:
  2878. The actor (person, bot, etc.) that is revoking access
  2879. [=object=]:
  2880. specific [[#Grant]] activities being undone, i.e. the access that they
  2881. granted is now disabled and it cannot be used anymore as the [=capability=]
  2882. of activities
  2883. [=capability=]:
  2884. A previously published `Grant`, giving the `actor` permission to disable
  2885. the [=object=] actor's access to the resource
  2886. </pre>
  2887. # Vocabulary # {#vocab}
  2888. ## Types
  2889. The base URI of all ForgeFed terms is `https://forgefed.org/ns#`.
  2890. The ForgeFed vocabulary has a JSON-LD context whose URI is
  2891. `https://forgefed.org/ns`. Implementers MUST either include the
  2892. ActivityPub and ForgeFed contexts in their object definitions, or other
  2893. contexts that would result with the ActivityPub and ForgeFed terms being
  2894. assigned they correct full URIs. Implementers MAY include additional contexts
  2895. and terms as appropriate.
  2896. A typical `@context` of a ForgeFed object may look like this:
  2897. <div class=example>
  2898. <xmp highlight=json-ld>
  2899. "@context": [
  2900. "https://www.w3.org/ns/activitystreams",
  2901. "https://forgefed.org/ns"
  2902. ]
  2903. </xmp>
  2904. </div>
  2905. ### Activities
  2906. #### Related to access control
  2907. <pre class=simpledef>
  2908. Name: <dfn dfn export>Grant</dfn>
  2909. URI: https://forgefed.org/ns#Grant
  2910. Extends: [=Activity=]
  2911. Description:
  2912. Indicates that [=target=] is being given (by the [=actor=]) access to a
  2913. resource specified by [=context=] under the role/permission specified by
  2914. [=object=].
  2915. </pre>
  2916. <div class=example>
  2917. <xmp highlight=json-ld line-highlight=7>
  2918. {
  2919. "@context": [
  2920. "https://www.w3.org/ns/activitystreams",
  2921. "https://forgefed.org/ns"
  2922. ],
  2923. "id": "https://example.dev/aviva/outbox/reBGo",
  2924. "type": "Grant",
  2925. "actor": "https://example.dev/aviva",
  2926. "to": [
  2927. "https://example.dev/aviva/followers",
  2928. "https://example.dev/aviva/myproject",
  2929. "https://example.dev/aviva/myproject/followers",
  2930. "https://example.dev/bob",
  2931. "https://example.dev/bob/followers"
  2932. ],
  2933. "object": "write",
  2934. "context": "https://example.dev/aviva/myproject",
  2935. "target": "https://example.dev/bob"
  2936. }
  2937. </xmp>
  2938. </div>
  2939. <pre class=simpledef>
  2940. Name: <dfn dfn export>Revoke</dfn>
  2941. URI: https://forgefed.org/ns#Revoke
  2942. Extends: [=Activity=]
  2943. Description:
  2944. Indicates that the [=actor=] is canceling [=target=]'s access to a resource
  2945. specified by [=context=] under the role specified by [=instrument=], making
  2946. the [=Grant=] activities specified by [=object=] unusable anymore in other
  2947. activities' [=capability=] field.
  2948. </pre>
  2949. <div class=example>
  2950. <xmp highlight=json-ld line-highlight=7>
  2951. {
  2952. "@context": [
  2953. "https://www.w3.org/ns/activitystreams",
  2954. "https://forgefed.org/ns"
  2955. ],
  2956. "id": "https://example.dev/myproject/outbox/nlTxb",
  2957. "type": "Revoke",
  2958. "actor": "https://example.dev/myproject",
  2959. "to": [
  2960. "https://example.dev/myproject/followers",
  2961. "https://example.dev/users/aviva"
  2962. ],
  2963. "object": "https://example.dev/myproject/outbox/reBGo",
  2964. "instrument": "https://example.dev/roles/developer",
  2965. "context": "https://example.dev/myproject",
  2966. "target": "https://example.dev/users/aviva"
  2967. }
  2968. </xmp>
  2969. </div>
  2970. #### Related to repositories
  2971. <pre class=simpledef>
  2972. Name: <dfn dfn export>Push</dfn>
  2973. URI: https://forgefed.org/ns#Push
  2974. Extends: [=Activity=]
  2975. Description:
  2976. Indicates that new content has been pushed to the [=Repository=].
  2977. </pre>
  2978. <div class=example>
  2979. <xmp highlight=json-ld line-highlight=7>
  2980. {
  2981. "@context": [
  2982. "https://www.w3.org/ns/activitystreams",
  2983. "https://forgefed.org/ns"
  2984. ],
  2985. "id": "https://example.dev/aviva/myproject/outbox/reBGo",
  2986. "type": "Push",
  2987. "actor": "https://example.dev/aviva/myproject",
  2988. "attributedTo": "https://example.dev/aviva",
  2989. "to": [
  2990. "https://example.dev/aviva",
  2991. "https://example.dev/aviva/followers",
  2992. "https://example.dev/aviva/myproject/followers"
  2993. ],
  2994. "summary": "<p>Aviva pushed a commit to myproject</p>",
  2995. "object": {
  2996. "type": "OrderedCollection",
  2997. "totalItems": 1,
  2998. "items": [
  2999. {
  3000. "id": "https://example.dev/aviva/myproject/commits/d96596230322716bd6f87a232a648ca9822a1c20",
  3001. "type": "Commit",
  3002. "attributedTo": "https://example.dev/aviva",
  3003. "context": "https://example.dev/aviva/myproject",
  3004. "hash": "d96596230322716bd6f87a232a648ca9822a1c20",
  3005. "created": "2019-11-03T13:43:59Z",
  3006. "summary": "Provide hints in sign-up form fields",
  3007. }
  3008. ]
  3009. },
  3010. "target": "https://example.dev/aviva/myproject/branches/master"
  3011. }
  3012. </xmp>
  3013. </div>
  3014. ### Actors
  3015. #### Software development components
  3016. <pre class=simpledef>
  3017. Name: <dfn dfn export>Repository</dfn>
  3018. URI: https://forgefed.org/ns#Repository
  3019. Extends: [=Object=]
  3020. Description:
  3021. Represents a version control system repository.
  3022. </pre>
  3023. <div class=example>
  3024. <xmp highlight=json-ld line-highlight=8>
  3025. {
  3026. "@context": [
  3027. "https://www.w3.org/ns/activitystreams",
  3028. "https://w3id.org/security/v1",
  3029. "https://forgefed.org/ns"
  3030. ],
  3031. "id": "https://dev.example/aviva/treesim",
  3032. "type": "Repository",
  3033. "publicKey": {
  3034. "id": "https://dev.example/aviva/treesim#main-key",
  3035. "owner": "https://dev.example/aviva/treesim",
  3036. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  3037. },
  3038. "inbox": "https://dev.example/aviva/treesim/inbox",
  3039. "outbox": "https://dev.example/aviva/treesim/outbox",
  3040. "followers": "https://dev.example/aviva/treesim/followers",
  3041. "team": "https://dev.example/aviva/treesim/team",
  3042. "name": "Tree Growth 3D Simulation",
  3043. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>"
  3044. }
  3045. </xmp>
  3046. </div>
  3047. <pre class=simpledef>
  3048. Name: <dfn dfn export>TicketTracker</dfn>
  3049. URI: https://forgefed.org/ns#TicketTracker
  3050. Extends: [=Object=]
  3051. Description:
  3052. Represents a [[wikipedia-ticket-tracker|ticket tracker]], i.e. a project
  3053. managing a list of work items.
  3054. </pre>
  3055. <div class=example>
  3056. <xmp highlight=json-ld line-highlight=8>
  3057. {
  3058. "@context": [
  3059. "https://www.w3.org/ns/activitystreams",
  3060. "https://w3id.org/security/v2",
  3061. "https://forgefed.org/ns"
  3062. ],
  3063. "id": "https://dev.example/aviva/treesim",
  3064. "type": ["Repository", "TicketTracker"],
  3065. "publicKey": {
  3066. "id": "https://dev.example/aviva/treesim#main-key",
  3067. "owner": "https://dev.example/aviva/treesim",
  3068. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  3069. },
  3070. "inbox": "https://dev.example/aviva/treesim/inbox",
  3071. "outbox": "https://dev.example/aviva/treesim/outbox",
  3072. "followers": "https://dev.example/aviva/treesim/followers",
  3073. "name": "Tree Growth 3D Simulation",
  3074. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>"
  3075. }
  3076. </xmp>
  3077. </div>
  3078. <pre class=simpledef>
  3079. Name: <dfn dfn export>PatchTracker</dfn>
  3080. URI: https://forgefed.org/ns#PatchTracker
  3081. Extends: [=Object=]
  3082. Description:
  3083. Represents a tracker of [[wikipedia-pr|merge requests]], i.e. a project
  3084. managing a list of patches or branches submitted as proposed changes to a
  3085. given [=Repository=].
  3086. </pre>
  3087. <div class=example>
  3088. <xmp highlight=json-ld line-highlight=8>
  3089. {
  3090. "@context": [
  3091. "https://www.w3.org/ns/activitystreams",
  3092. "https://w3id.org/security/v2",
  3093. "https://forgefed.org/ns"
  3094. ],
  3095. "id": "https://dev.example/aviva/treesim",
  3096. "type": ["Repository", "TicketTracker", "PatchTracker"],
  3097. "publicKey": {
  3098. "id": "https://dev.example/aviva/treesim#main-key",
  3099. "owner": "https://dev.example/aviva/treesim",
  3100. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  3101. },
  3102. "inbox": "https://dev.example/aviva/treesim/inbox",
  3103. "outbox": "https://dev.example/aviva/treesim/outbox",
  3104. "followers": "https://dev.example/aviva/treesim/followers",
  3105. "name": "Tree Growth 3D Simulation",
  3106. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>"
  3107. }
  3108. </xmp>
  3109. </div>
  3110. #### Organizational structure tools
  3111. <pre class=simpledef>
  3112. Name: <dfn dfn export>Project</dfn>
  3113. URI: https://forgefed.org/ns#Project
  3114. Extends: [=Object=]
  3115. Description:
  3116. Represents a project, a planned endeavor that involves usage of tools
  3117. related to the software development lifecycle. It may be a software
  3118. project, but may also be totally unrelated to software development. For
  3119. example, it may be a book that is being written using Markdown files kept
  3120. in a Git repository. A [=Project=] object is a way to collect forge related
  3121. components together under one title.
  3122. </pre>
  3123. <div class=example>
  3124. <xmp highlight=json-ld line-highlight=8>
  3125. {
  3126. "@context": [
  3127. "https://www.w3.org/ns/activitystreams",
  3128. "https://w3id.org/security/v2",
  3129. "https://forgefed.org/ns"
  3130. ],
  3131. "id": "https://dev.example/projects/wanderer",
  3132. "type": "Project",
  3133. "name": "Wanderer",
  3134. "summary": "3D nature exploration game",
  3135. "components": {
  3136. "type": "Collection",
  3137. "totalItems": 7,
  3138. "items": [
  3139. "https://dev.example/repos/opengl-vegetation",
  3140. "https://dev.example/repos/opengl-vegetation/patch-tracker",
  3141. "https://dev.example/repos/treesim",
  3142. "https://dev.example/repos/treesim/patch-tracker",
  3143. "https://dev.example/repos/wanderer",
  3144. "https://dev.example/repos/wanderer/patch-tracker",
  3145. "https://dev.example/issue-trackers/wanderer"
  3146. ]
  3147. },
  3148. "publicKey": {
  3149. "id": "https://dev.example/projects/wanderer#main-key",
  3150. "owner": "https://dev.example/projects/wanderer",
  3151. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  3152. },
  3153. "inbox": "https://dev.example/projects/wanderer/inbox",
  3154. "outbox": "https://dev.example/projects/wanderer/outbox",
  3155. "followers": "https://dev.example/projects/wanderer/followers"
  3156. }
  3157. </xmp>
  3158. </div>
  3159. <pre class=simpledef>
  3160. Name: <dfn dfn export>Team</dfn>
  3161. URI: https://forgefed.org/ns#Team
  3162. Extends: [=Object=]
  3163. Description:
  3164. Represents a group of people working together, collaborating on shared
  3165. resources. Each member [=Person=] in the team has a defined role, affecting
  3166. the level of access they have to the team's shared resources and to
  3167. managing the team itself.
  3168. </pre>
  3169. <div class=example>
  3170. <xmp highlight=json-ld line-highlight=8>
  3171. {
  3172. "@context": [
  3173. "https://www.w3.org/ns/activitystreams",
  3174. "https://w3id.org/security/v2",
  3175. "https://forgefed.org/ns"
  3176. ],
  3177. "id": "https://dev.example/teams/mobilizon-dev-team",
  3178. "type": "Team",
  3179. "name": "Mobilizon Development Team",
  3180. "summary": "We're creating a federated tool for organizing events!",
  3181. "members": {
  3182. "type": "Collection",
  3183. "totalItems": 3,
  3184. "items": [
  3185. { "type": "Relationship",
  3186. "subject": "https://dev.example/teams/mobilizon-dev-team",
  3187. "relationship": "hasMember",
  3188. "object": "https://dev.example/people/alice",
  3189. "tag": "https://roles.example/admin"
  3190. },
  3191. { "type": "Relationship",
  3192. "subject": "https://dev.example/teams/mobilizon-dev-team",
  3193. "relationship": "hasMember",
  3194. "object": "https://dev.example/people/bob",
  3195. "tag": "maintain"
  3196. },
  3197. { "type": "Relationship",
  3198. "subject": "https://dev.example/teams/mobilizon-dev-team",
  3199. "relationship": "hasMember",
  3200. "object": "https://dev.example/people/celine",
  3201. "tag": "develop"
  3202. }
  3203. ]
  3204. },
  3205. "subteams": {
  3206. "type": "Collection",
  3207. "totalItems": 2,
  3208. "items": [
  3209. "https://dev.example/teams/mobilizon-backend-team",
  3210. "https://dev.example/teams/mobilizon-frontend-team"
  3211. ]
  3212. },
  3213. "context": "https://dev.example/teams/framasoft-developers",
  3214. "publicKey": {
  3215. "id": "https://dev.example/teams/mobilizon-dev-team#main-key",
  3216. "owner": "https://dev.example/teams/mobilizon-dev-team",
  3217. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  3218. },
  3219. "inbox": "https://dev.example/teams/mobilizon-dev-team/inbox",
  3220. "outbox": "https://dev.example/teams/mobilizon-dev-team/outbox",
  3221. "followers": "https://dev.example/teams/mobilizon-dev-team/followers"
  3222. }
  3223. </xmp>
  3224. </div>
  3225. ### Objects
  3226. <pre class=simpledef>
  3227. Name: <dfn dfn export>CapabilityUsage</dfn>
  3228. URI: https://forgefed.org/ns#CapabilityUsage
  3229. Extends: [=Object=]
  3230. Values:
  3231. This specification defines 3&#x3a; [=gatherAndConvey=], [=distribute=] and
  3232. [=invoke]
  3233. Description:
  3234. Represents a mode of using a [=Grant=] as an Object Capability (OCAP).
  3235. There are two conceptual operations for `Grant`s&#x3a; Invocation (acting
  3236. on the resource under the specified role) and Delegation (passing on the
  3237. access to more actors, possibly with reduced privileges). A value of this
  3238. type refers to one or both of these operations, and possibly to more
  3239. specific conditions and restrictions on applying them.
  3240. </pre>
  3241. <div class=example>
  3242. <xmp highlight=json-ld line-highlight=7>
  3243. TODO
  3244. </xmp>
  3245. </div>
  3246. <pre class=simpledef>
  3247. Name: <dfn dfn export>Role</dfn>
  3248. URI: https://forgefed.org/ns#Role
  3249. Extends: [=Object=]
  3250. Values:
  3251. [=visit=], [=report=], [=triage=], [=write=], [=maintain=], [=admin=],
  3252. [=delegate=]
  3253. Description:
  3254. Represents a role that an actor has within a [=Team=], or a role defining
  3255. the level of access an actor has to a resource.
  3256. </pre>
  3257. <div class=example>
  3258. <xmp highlight=json-ld line-highlight=7>
  3259. TODO
  3260. </xmp>
  3261. </div>
  3262. <pre class=simpledef>
  3263. Name: <dfn dfn export>Branch</dfn>
  3264. URI: https://forgefed.org/ns#Branch
  3265. Extends: [=Object=]
  3266. Description:
  3267. Represents a named variable reference to a version of the [=Repository=],
  3268. typically used for committing changes in parallel to other development, and
  3269. usually eventually merging the changes into the main history line.
  3270. </pre>
  3271. <div class=example>
  3272. <xmp highlight=json-ld line-highlight=7>
  3273. {
  3274. "@context": [
  3275. "https://www.w3.org/ns/activitystreams",
  3276. "https://forgefed.org/ns"
  3277. ],
  3278. "id": "https://example.dev/luke/myrepo/branches/master",
  3279. "type": "Branch",
  3280. "name": "master",
  3281. "context": "https://example.dev/luke/myrepo",
  3282. "ref": "refs/heads/master"
  3283. }
  3284. </xmp>
  3285. </div>
  3286. <pre class=simpledef>
  3287. Name: <dfn dfn export>Commit</dfn>
  3288. URI: https://forgefed.org/ns#Commit
  3289. Extends: [=Object=]
  3290. Description:
  3291. Represents a named set of changes in the history of a [=Repository=]. This
  3292. is called "commit" in Git, Mercurial and Monotone; "patch" in Darcs;
  3293. sometimes called "change set". Note that `Commit` is a set of changes that
  3294. already exists in a repo's history, while a [=Patch=] is a separate
  3295. proposed change set, that *could* be applied and pushed to a repo,
  3296. resulting with a `Commit`.
  3297. </pre>
  3298. <div class=example>
  3299. <xmp highlight=json-ld line-highlight=7>
  3300. {
  3301. "@context": [
  3302. "https://www.w3.org/ns/activitystreams",
  3303. "https://forgefed.org/ns"
  3304. ],
  3305. "id": "https://example.dev/alice/myrepo/commits/109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
  3306. "type": "Commit",
  3307. "context": "https://example.dev/alice/myrepo",
  3308. "attributedTo": "https://example.dev/bob",
  3309. "committedBy": "https://example.dev/alice",
  3310. "hash": "109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
  3311. "summary": "Add an installation script, fixes issue #89",
  3312. "description": {
  3313. "mediaType": "text/plain",
  3314. "content": "It's about time people can install on their computers!"
  3315. },
  3316. "created": "2019-07-11T12:34:56Z",
  3317. "committed": "2019-07-26T23:45:01Z"
  3318. }
  3319. </xmp>
  3320. </div>
  3321. <pre class=simpledef>
  3322. Name: <dfn dfn export>Patch</dfn>
  3323. URI: https://forgefed.org/ns#Patch
  3324. Extends: [=Object=]
  3325. Description:
  3326. Represents a named set of changes that are being proposed for applying to a
  3327. specific [=Repository=].
  3328. </pre>
  3329. <div class=example>
  3330. <xmp highlight=json-ld line-highlight=7>
  3331. {
  3332. "@context": [
  3333. "https://www.w3.org/ns/activitystreams",
  3334. "https://forgefed.org/ns"
  3335. ],
  3336. "id": "https://dev.example/aviva/game-of-life/pulls/825/versions/1/patches/1",
  3337. "type": "Patch",
  3338. "attributedTo": "https://forge.example/luke",
  3339. "context": "https://dev.example/aviva/game-of-life/pulls/825/versions/1",
  3340. "mediaType": "application/x-git-patch",
  3341. "content": "From c9ae5f4ff4a330b6e1196ceb7db1665bd4c1..."
  3342. }
  3343. </xmp>
  3344. </div>
  3345. <pre class=simpledef>
  3346. Name: <dfn dfn export>TicketDependency</dfn>
  3347. URI: https://forgefed.org/ns#TicketDependency
  3348. Extends: [=Relationship=]
  3349. Description:
  3350. Represents a relationship between 2 [=Ticket=]s, in which the resolution of
  3351. one ticket requires the other ticket to be resolved too. It MUST specify
  3352. the [=subject=], [=object=] and [=relationship=] properties, and the
  3353. `relationship` property MUST be [=dependsOn=].
  3354. </pre>
  3355. <div class=example>
  3356. <xmp highlight=json-ld line-highlight=6>
  3357. {
  3358. "@context": [
  3359. "https://www.w3.org/ns/activitystreams",
  3360. "https://forgefed.org/ns"
  3361. ],
  3362. "type": ["Relationship", "TicketDependency"],
  3363. "id": "https://example.dev/ticket-deps/2342593",
  3364. "attributedTo": "https://example.dev/alice",
  3365. "summary": "Alice's ticket depends on Bob's ticket",
  3366. "published": "2019-07-11T12:34:56Z",
  3367. "subject": "https://example.dev/alice/myproj/issues/42",
  3368. "relationship": "dependsOn",
  3369. "object": "https://dev.community/bob/coolproj/issues/85"
  3370. }
  3371. </xmp>
  3372. </div>
  3373. <pre class=simpledef>
  3374. Name: <dfn dfn export>Ticket</dfn>
  3375. URI: https://forgefed.org/ns#Ticket
  3376. Extends: [=Object=]
  3377. Description:
  3378. Represents an item that requires work or attention. Tickets exist in the
  3379. context of a project (which may or may not be a version-control
  3380. repository), and are used to track ideas, proposals, tasks, bugs and more.
  3381. </pre>
  3382. <div class=example>
  3383. <xmp highlight=json-ld line-highlight=6>
  3384. {
  3385. "@context": [
  3386. "https://www.w3.org/ns/activitystreams",
  3387. "https://forgefed.org/ns"
  3388. ],
  3389. "type": "Ticket",
  3390. "id": "https://example.dev/alice/myrepo/issues/42",
  3391. "context": "https://example.dev/alice/myrepo",
  3392. "attributedTo": "https://dev.community/bob",
  3393. "summary": "Nothing works!",
  3394. "content": "<p>Please fix. <i>Everything</i> is broken!</p>",
  3395. "mediaType": "text/html",
  3396. "source": {
  3397. "content": "Please fix. *Everything* is broken!",
  3398. "mediaType": "text/markdown; variant=CommonMark"
  3399. },
  3400. "assignedTo": "https://example.dev/alice",
  3401. "isResolved": false
  3402. }
  3403. </xmp>
  3404. </div>
  3405. ## Properties
  3406. ### General-purpose
  3407. <pre class=simpledef>
  3408. Name: <dfn dfn export>earlyItems</dfn>
  3409. URI: https://forgefed.org/ns#earlyItems
  3410. Domain: [=OrderedCollection=]
  3411. Range: Ordered list of [ [=Object=] | [=Link=] ]
  3412. Functional: No
  3413. Inverse of: None
  3414. Description:
  3415. In an ordered collection (or an ordered collection page) in which [=items=]
  3416. (or [=orderedItems=]) contains a continuous subset of the collection's
  3417. items from one end, `earlyItems` identifiers a continuous subset from the
  3418. other end. For example, if `items` lists the chronologically latest items,
  3419. `earlyItems` would list the chrologically earliest items. The ordering rule
  3420. for items in `earlyItems` MUST be the same as in `items`. For examle, if
  3421. `items` lists items in reverse chronogical order, then so does
  3422. `earlyItems`.
  3423. </pre>
  3424. <div class=example>
  3425. <xmp highlight=json-ld line-highlight=14>
  3426. {
  3427. "@context": [
  3428. "https://www.w3.org/ns/activitystreams",
  3429. "https://forgefed.org/ns"
  3430. ],
  3431. "id": "https://dev.example/aviva/outbox",
  3432. "type": "OrderedCollection",
  3433. "totalItems": 712,
  3434. "orderedItems": [
  3435. "https://dev.example/aviva/outbox/712",
  3436. "https://dev.example/aviva/outbox/711",
  3437. "https://dev.example/aviva/outbox/710"
  3438. ],
  3439. "earlyItems": [
  3440. "https://dev.example/aviva/outbox/3",
  3441. "https://dev.example/aviva/outbox/2",
  3442. "https://dev.example/aviva/outbox/1"
  3443. ]
  3444. }
  3445. </xmp>
  3446. </div>
  3447. <pre class=simpledef>
  3448. Name: <dfn dfn export>previousVersions</dfn>
  3449. URI: https://forgefed.org/ns#previousVersions
  3450. Domain: [=Object=]
  3451. Range: `rdf:List` of objects of the same `@type` as the subject
  3452. Functional: Yes
  3453. Inverse of: None, but see [=currentVersion=]
  3454. Description:
  3455. Specifies the previous versions of the subject, as an ordered list in
  3456. reverse chronological order.
  3457. </pre>
  3458. <div class=example>
  3459. <xmp highlight=json-ld line-highlight=10>
  3460. {
  3461. "@context": [
  3462. "https://www.w3.org/ns/activitystreams",
  3463. "https://forgefed.org/ns"
  3464. ],
  3465. "id": "https://dev.example/aviva/notes/107",
  3466. "type": "Note",
  3467. "attributedTo": "https://dev.example/aviva",
  3468. "content": "I agree!!!!! (edit: fixed a typo)",
  3469. "previousVersions": [
  3470. "https://dev.example/aviva/notes/107_old_version",
  3471. "https://dev.example/aviva/notes/107_very_old_version",
  3472. "https://dev.example/aviva/notes/107_ancient_version"
  3473. ]
  3474. }
  3475. </xmp>
  3476. </div>
  3477. <pre class=simpledef>
  3478. Name: <dfn dfn export>currentVersion</dfn>
  3479. URI: https://forgefed.org/ns#currentVersion
  3480. Domain: [=Object=]
  3481. Range: [=Object=], of the same `@type` as the subject
  3482. Functional: Yes
  3483. Inverse of: None, but see [=previousVersions=]
  3484. Description:
  3485. Specifies the latest. current, up-to-date version of the subject. Once the
  3486. subject specifies the `currentVersion` property, it SHOULD NOT get any
  3487. changes to any other properties. The exception is `currentVersion` itself,
  3488. which MUST be updated whenever needed, to always point to the latest
  3489. version.
  3490. </pre>
  3491. <div class=example>
  3492. <xmp highlight=json-ld line-highlight=10>
  3493. {
  3494. "@context": [
  3495. "https://www.w3.org/ns/activitystreams",
  3496. "https://forgefed.org/ns"
  3497. ],
  3498. "id": "https://dev.example/aviva/notes/107_old_version",
  3499. "type": "Note",
  3500. "attributedTo": "https://dev.example/aviva",
  3501. "content": "I agree!!111",
  3502. "currentVersion": "https://dev.example/aviva/notes/107"
  3503. }
  3504. </xmp>
  3505. </div>
  3506. ### Projects
  3507. <pre class=simpledef>
  3508. Name: <dfn dfn export>components</dfn>
  3509. URI: https://forgefed.org/ns#components
  3510. Domain: [=Project=]
  3511. Range: [=Collection=]
  3512. Functional: Yes
  3513. Inverse of:
  3514. None, but see the usage of [=context=] in the Modeling specification, e.g.
  3515. in [[#Repository]]
  3516. Description:
  3517. Identifies a [=Collection=] listing actors whose services and resources are
  3518. considered to be components of this project.
  3519. </pre>
  3520. <div class=example>
  3521. <xmp highlight=json-ld line-highlight=11>
  3522. {
  3523. "@context": [
  3524. "https://www.w3.org/ns/activitystreams",
  3525. "https://forgefed.org/ns"
  3526. ],
  3527. "id": "https://dev.example/projects/wanderer",
  3528. "type": "Project",
  3529. "name": "Wanderer",
  3530. "summary": "3D nature exploration game",
  3531. "components": {
  3532. "type": "Collection",
  3533. "totalItems": 7,
  3534. "items": [
  3535. "https://dev.example/repos/opengl-vegetation",
  3536. "https://dev.example/repos/opengl-vegetation/patch-tracker",
  3537. "https://dev.example/repos/treesim",
  3538. "https://dev.example/repos/treesim/patch-tracker",
  3539. "https://dev.example/repos/wanderer",
  3540. "https://dev.example/repos/wanderer/patch-tracker",
  3541. "https://dev.example/issue-trackers/wanderer"
  3542. ]
  3543. },
  3544. "inbox": "https://dev.example/projects/wanderer/inbox",
  3545. "outbox": "https://dev.example/projects/wanderer/outbox",
  3546. "followers": "https://dev.example/projects/wanderer/followers"
  3547. }
  3548. </xmp>
  3549. </div>
  3550. <pre class=simpledef>
  3551. Name: <dfn dfn export>subprojects</dfn>
  3552. URI: https://forgefed.org/ns#subprojects
  3553. Domain: [=Project=]
  3554. Range: [=Collection=] of [=Project=]s
  3555. Functional: Yes
  3556. Inverse of:
  3557. None, but see the usage of [=context=] in [[#Project]]
  3558. Description:
  3559. Identifies a [=Collection=] listing the subprojects of this [=Project=].
  3560. </pre>
  3561. <div class=example>
  3562. <xmp highlight=json-ld line-highlight=11>
  3563. {
  3564. "@context": [
  3565. "https://www.w3.org/ns/activitystreams",
  3566. "https://forgefed.org/ns"
  3567. ],
  3568. "id": "https://dev.example/projects/wanderer",
  3569. "type": "Project",
  3570. "name": "Wanderer",
  3571. "summary": "3D nature exploration game",
  3572. "subprojects": {
  3573. "type": "Collection",
  3574. "totalItems": 2,
  3575. "items": [
  3576. "https://dev.example/projects/nature-3d-models",
  3577. "https://dev.example/projects/wanderer-fundraising"
  3578. ]
  3579. },
  3580. "inbox": "https://dev.example/projects/wanderer/inbox",
  3581. "outbox": "https://dev.example/projects/wanderer/outbox",
  3582. "followers": "https://dev.example/projects/wanderer/followers"
  3583. }
  3584. </xmp>
  3585. </div>
  3586. ### Team membership and nesting
  3587. <pre class=simpledef>
  3588. Name: <dfn dfn export>hasMember</dfn>
  3589. URI: https://forgefed.org/ns#hasMember
  3590. Domain: [=Team=]
  3591. Range: [=Person=]
  3592. Functional: No
  3593. Inverse of: None
  3594. Description:
  3595. Identifier a [=Person=] who is a member of this [=Team=].
  3596. </pre>
  3597. <div class=example>
  3598. <xmp highlight=json-ld line-highlight=9>
  3599. TODO
  3600. </xmp>
  3601. </div>
  3602. <pre class=simpledef>
  3603. Name: <dfn dfn export>members</dfn>
  3604. URI: https://forgefed.org/ns#members
  3605. Domain: [=Team=]
  3606. Range:
  3607. [=Collection=] of [=Relationship=]s whose [=relationship=] is [=hasMember=]
  3608. and whose [=subject=] is this `Team`.
  3609. Functional: Yes
  3610. Inverse of: None
  3611. Description:
  3612. Identifies a collection of the members of this [=Team=], represented as
  3613. [=hasMember=] [=Relationship=]s.
  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>subteams</dfn>
  3622. URI: https://forgefed.org/ns#subteams
  3623. Domain: [=Team=]
  3624. Range: [=Collection=] of [=Team=]s.
  3625. Functional: Yes
  3626. Inverse of: None
  3627. Description:
  3628. Identifies a collection of the subteams of this [=Team=], i.e. teams whose
  3629. members inherit the access that this team's members have to projects and to
  3630. project components (such as [=Repository=]s).
  3631. </pre>
  3632. <div class=example>
  3633. <xmp highlight=json-ld line-highlight=9>
  3634. TODO
  3635. </xmp>
  3636. </div>
  3637. ### Ticket assignment and resolution
  3638. <pre class=simpledef>
  3639. Name: <dfn dfn export>assignedTo</dfn>
  3640. URI: https://forgefed.org/ns#assignedTo
  3641. Domain: [=Ticket=]
  3642. Range: [=Person=]
  3643. Functional: Yes
  3644. Inverse of: None
  3645. Description:
  3646. Identifies the [=Person=] assigned to work on this [=Ticket=].
  3647. </pre>
  3648. <div class=example>
  3649. <xmp highlight=json-ld line-highlight=9>
  3650. TODO
  3651. </xmp>
  3652. </div>
  3653. <pre class=simpledef>
  3654. Name: <dfn dfn export>isResolved</dfn>
  3655. URI: https://forgefed.org/ns#isResolved
  3656. Domain: [=Ticket=]
  3657. Range: `xsd:boolean`
  3658. Functional: Yes
  3659. Inverse of: None
  3660. Description:
  3661. Specifies whether the [=Ticket=] is closed, i.e. the work on it is done and
  3662. it doesn't need to attract attention anymore.
  3663. </pre>
  3664. <div class=example>
  3665. <xmp highlight=json-ld line-highlight=9>
  3666. TODO
  3667. </xmp>
  3668. </div>
  3669. <pre class=simpledef>
  3670. Name: <dfn dfn export>resolvedBy</dfn>
  3671. URI: https://forgefed.org/ns#resolvedBy
  3672. Domain: [=Ticket=]
  3673. Range: [=Object=] than is an actor, or [=Activity=]
  3674. Functional: Yes
  3675. Inverse of: None
  3676. Description:
  3677. Identifies the Actor who has resolved the [=Ticket=], or the activity that
  3678. has resolved the Ticket.
  3679. </pre>
  3680. <div class=example>
  3681. <xmp highlight=json-ld line-highlight=9>
  3682. TODO
  3683. </xmp>
  3684. </div>
  3685. <pre class=simpledef>
  3686. Name: <dfn dfn export>resolved</dfn>
  3687. URI: https://forgefed.org/ns#resolved
  3688. Domain: [=Ticket=]
  3689. Range: `xsd:dateTime`
  3690. Functional: Yes
  3691. Inverse of: None
  3692. Description:
  3693. For a resolved [=Ticket=], specifies the time the Ticket has been resolved.
  3694. </pre>
  3695. <div class=example>
  3696. <xmp highlight=json-ld line-highlight=9>
  3697. TODO
  3698. </xmp>
  3699. </div>
  3700. ### Ticket dependencies
  3701. <pre class=simpledef>
  3702. Name: <dfn dfn export>dependsOn</dfn>
  3703. URI: https://forgefed.org/ns#dependsOn
  3704. Domain: [=Ticket=]
  3705. Range: [=Ticket=]
  3706. Functional: No
  3707. Inverse of: [=dependedBy=]
  3708. Description:
  3709. Identifies one or more tickets on which this [=Ticket=] depends, i.e. it
  3710. can't be resolved without those tickets being resolved too.
  3711. </pre>
  3712. <div class=example>
  3713. <xmp highlight=json-ld line-highlight=9>
  3714. TODO
  3715. </xmp>
  3716. </div>
  3717. <pre class=simpledef>
  3718. Name: <dfn dfn export>dependedBy</dfn>
  3719. URI: https://forgefed.org/ns#dependedBy
  3720. Domain: [=Ticket=]
  3721. Range: [=Ticket=]
  3722. Functional: No
  3723. Inverse of: [=dependsOn=]
  3724. Description:
  3725. Identifies one or more tickets which depend on this [=Ticket=], i.e. they
  3726. can't be resolved without this tickets being resolved too.
  3727. </pre>
  3728. <div class=example>
  3729. <xmp highlight=json-ld line-highlight=9>
  3730. TODO
  3731. </xmp>
  3732. </div>
  3733. <pre class=simpledef>
  3734. Name: <dfn dfn export>dependencies</dfn>
  3735. URI: https://forgefed.org/ns#dependencies
  3736. Domain: [=Ticket=]
  3737. Range: [=Collection=] of items of type [=TicketDependency=]
  3738. Functional: Yes
  3739. Inverse of: None
  3740. Description:
  3741. Identifies a [=Collection=] of [=TicketDependency=] which specify tickets
  3742. that this [=Ticket=] depends on, i.e. this ticket is the [=subject=] of the
  3743. [=dependsOn=] relationship.
  3744. </pre>
  3745. <div class=example>
  3746. <xmp highlight=json-ld line-highlight=9>
  3747. TODO
  3748. </xmp>
  3749. </div>
  3750. <pre class=simpledef>
  3751. Name: <dfn dfn export>dependants</dfn>
  3752. URI: https://forgefed.org/ns#dependants
  3753. Domain: [=Ticket=]
  3754. Range: [=Collection=] of items of type [=TicketDependency=]
  3755. Functional: Yes
  3756. Inverse of: None
  3757. Description:
  3758. Identifies a [=Collection=] of [=TicketDependency=] which specify tickets
  3759. that depends on this [=Ticket=], i.e. this ticket is the [=object=] of the
  3760. [=dependsOn=] relationship. Often called "reverse dependencies".
  3761. </pre>
  3762. <div class=example>
  3763. <xmp highlight=json-ld line-highlight=9>
  3764. TODO
  3765. </xmp>
  3766. </div>
  3767. ### Repository cloning
  3768. <pre class=simpledef>
  3769. Name: <dfn dfn export>cloneUri</dfn>
  3770. URI: https://forgefed.org/ns#cloneUri
  3771. Domain: [=Repository=]
  3772. Range: [=Object=]
  3773. Functional: No
  3774. Inverse of: None
  3775. Description:
  3776. An endpoint that can be used with a VCS protocol such as Git or Mercurial
  3777. to access a given repository.
  3778. </pre>
  3779. <div class=example>
  3780. <xmp highlight=json-ld line-highlight=9>
  3781. TODO
  3782. </xmp>
  3783. </div>
  3784. ### Repository pushing
  3785. <pre class=simpledef>
  3786. Name: <dfn dfn export>hashBefore</dfn>
  3787. URI: https://forgefed.org/ns#hashBefore
  3788. Domain: [=Push=]
  3789. Range: `xsd:string` of hexadecimal digit ASCII characters
  3790. Functional: Yes
  3791. Inverse of: None
  3792. Description:
  3793. Specifies the hash of the commit that the pushed branch/repo was pointing
  3794. to *right before* the push happpened.
  3795. </pre>
  3796. <div class=example>
  3797. <xmp highlight=json-ld line-highlight=9>
  3798. TODO
  3799. </xmp>
  3800. </div>
  3801. <pre class=simpledef>
  3802. Name: <dfn dfn export>hashAfter</dfn>
  3803. URI: https://forgefed.org/ns#hashAfter
  3804. Domain: [=Push=]
  3805. Range: `xsd:string` of hexadecimal digit ASCII characters
  3806. Functional: Yes
  3807. Inverse of: None
  3808. Description:
  3809. Specifies the hash of the commit that the pushed branch/repo was pointing
  3810. to *right after* the push happpened.
  3811. </pre>
  3812. <div class=example>
  3813. <xmp highlight=json-ld line-highlight=9>
  3814. TODO
  3815. </xmp>
  3816. </div>
  3817. ### Commits and branches
  3818. <pre class=simpledef>
  3819. Name: <dfn dfn export lt="prop-repository">repository</dfn> (DEPRECATED)
  3820. URI: https://forgefed.org/ns#repository
  3821. Domain: [=Commit=]
  3822. Range: [=Repository=]
  3823. Functional: Yes
  3824. Inverse of: None
  3825. Description:
  3826. Identifies the repository to which a commit belongs. DEPRECATED: Use the
  3827. standard ActivityPub `context` property instead.
  3828. </pre>
  3829. <div class=example>
  3830. <xmp highlight=json-ld line-highlight=9>
  3831. TODO
  3832. </xmp>
  3833. </div>
  3834. <pre class=simpledef>
  3835. Name: <dfn dfn export>description</dfn>
  3836. URI: https://forgefed.org/ns#description
  3837. Domain: [=Commit=]
  3838. Range:
  3839. [=Object=], specifying [=content=] and [=mediaType=]. The `mediaType`
  3840. SHOULD be `"text/plain"`.
  3841. Functional: Yes
  3842. Inverse of: None
  3843. Description:
  3844. Specifies the description text of a [=Commit=], which is an optional
  3845. possibly multi-line text provided in addition to the one-line commit title.
  3846. The range of the `description` property works the same way the range of the
  3847. ActivityPub [=source=] property works.
  3848. </pre>
  3849. <div class=example>
  3850. <xmp highlight=json-ld line-highlight=14>
  3851. {
  3852. "@context": [
  3853. "https://www.w3.org/ns/activitystreams",
  3854. "https://forgefed.org/ns"
  3855. ],
  3856. "id": "https://example.dev/alice/myrepo/commits/109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
  3857. "type": "Commit",
  3858. "context": "https://example.dev/alice/myrepo",
  3859. "attributedTo": "https://example.dev/bob",
  3860. "hash": "109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
  3861. "created": "2019-07-11T12:34:56Z",
  3862. "summary": "Add an installation script, fixes issue #89",
  3863. "description": {
  3864. "mediaType": "text/plain",
  3865. "content": "It's about time people can install on their computers!"
  3866. },
  3867. }
  3868. </xmp>
  3869. </div>
  3870. <pre class=simpledef>
  3871. Name: <dfn dfn export>committedBy</dfn>
  3872. URI: https://forgefed.org/ns#committedBy
  3873. Domain: [=Commit=]
  3874. Range: [=Object=]
  3875. Functional: Yes
  3876. Inverse of: None
  3877. Description:
  3878. Identifies the actor (usually a person, but could be something else, e.g. a
  3879. bot) that added a set of changes to the version-control [=Repository=].
  3880. Sometimes the author of the changes and the committer of those changes
  3881. aren't the same actor, in which case the `committedBy` property can be used
  3882. to specify who added the changes to the repository. For example, when
  3883. applying a patch to a repository, e.g. a Git repository, the author would
  3884. be the person who made the patch, and the committer would be the person who
  3885. applied the patch to their copy of the repository.
  3886. </pre>
  3887. <div class=example>
  3888. <xmp highlight=json-ld line-highlight=9>
  3889. TODO
  3890. </xmp>
  3891. </div>
  3892. <pre class=simpledef>
  3893. Name: <dfn dfn export>hash</dfn>
  3894. URI: https://forgefed.org/ns#hash
  3895. Domain: [=Commit=]
  3896. Range: `xsd:string` of hexadecimal digit ASCII characters
  3897. Functional: Yes
  3898. Inverse of: None
  3899. Description:
  3900. Specifies the hash associated with a [=Commit=], which is a unique
  3901. identifier of the commit within the [=Repository=], usually generated as a
  3902. cryptographic hash function of some (or all) of the commit's data or
  3903. metadata. For example, in Git it would be the SHA1 hash of the commit; in
  3904. Darcs it would be the SHA1 hash of the patch info.
  3905. </pre>
  3906. <div class=example>
  3907. <xmp highlight=json-ld line-highlight=9>
  3908. TODO
  3909. </xmp>
  3910. </div>
  3911. <pre class=simpledef>
  3912. Name: <dfn dfn export>committed</dfn>
  3913. URI: https://forgefed.org/ns#committed
  3914. Domain: [=Commit=]
  3915. Range: `xsd:dateTime`
  3916. Functional: Yes
  3917. Inverse of: None
  3918. Description:
  3919. Specifies the time that a set of changes was committed into the
  3920. [=Repository=] and became a [=Commit=] in it. This can be different from
  3921. the time the set of changes was produced, e.g. if one person creates a
  3922. patch and sends to another, and the other person then applies the patch to
  3923. their copy of the repository. We call the former event "created" and the
  3924. latter event "committed", and this latter event is specified by the
  3925. `committed` property.
  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>filesAdded</dfn>
  3934. URI: https://forgefed.org/ns#filesAdded
  3935. Domain: [=Commit=]
  3936. Range: `xsd:string`
  3937. Functional: No
  3938. Inverse of: None
  3939. Description:
  3940. Specifies a filename, as a relative path, relative to the top of the tree
  3941. of files in the [=Repository=], of a file that got added in this
  3942. [=Commit=], and didn't exist in the previous version of the tree.
  3943. </pre>
  3944. <div class=example>
  3945. <xmp highlight=json-ld line-highlight=9>
  3946. TODO
  3947. </xmp>
  3948. </div>
  3949. <pre class=simpledef>
  3950. Name: <dfn dfn export>filesModified</dfn>
  3951. URI: https://forgefed.org/ns#filesModified
  3952. Domain: [=Commit=]
  3953. Range: `xsd:string`
  3954. Functional: No
  3955. Inverse of: None
  3956. Description:
  3957. Specifies a filename, as a relative path, relative to the top of the tree
  3958. of files in the [=Repository=], of a file that existed in the previous
  3959. version of the tree, and its contents got modified in this [=Commit=].
  3960. </pre>
  3961. <div class=example>
  3962. <xmp highlight=json-ld line-highlight=9>
  3963. TODO
  3964. </xmp>
  3965. </div>
  3966. <pre class=simpledef>
  3967. Name: <dfn dfn export>filesRemoved</dfn>
  3968. URI: https://forgefed.org/ns#filesRemoved
  3969. Domain: [=Commit=]
  3970. Range: `xsd:string`
  3971. Functional: No
  3972. Inverse of: None
  3973. Description:
  3974. Specifies a filename, as a relative path, relative to the top of the tree
  3975. of files in the [=Repository=], of a file that existed in the previous
  3976. version of the tree, and got removed from the tree in this [=Commit=].
  3977. </pre>
  3978. <div class=example>
  3979. <xmp highlight=json-ld line-highlight=9>
  3980. TODO
  3981. </xmp>
  3982. </div>
  3983. <pre class=simpledef>
  3984. Name: <dfn dfn export>ref</dfn>
  3985. URI: https://forgefed.org/ns#ref
  3986. Domain: [=Branch=]
  3987. Range: `xsd:string`
  3988. Functional: Yes
  3989. Inverse of: None
  3990. Description:
  3991. Specifies an identifier for a [=Branch=], that is used in the
  3992. [=Repository=] to uniquely refer to it. For example, in Git,
  3993. "refs/heads/master" would be the `ref` of the master branch.
  3994. </pre>
  3995. <div class=example>
  3996. <xmp highlight=json-ld line-highlight=11>
  3997. {
  3998. "@context": [
  3999. "https://www.w3.org/ns/activitystreams",
  4000. "https://forgefed.org/ns"
  4001. ],
  4002. "id": "https://example.dev/luke/myrepo/branches/master",
  4003. "type": "Branch",
  4004. "name": "master",
  4005. "context": "https://example.dev/luke/myrepo",
  4006. "ref": "refs/heads/master"
  4007. }
  4008. </xmp>
  4009. </div>
  4010. ### Activity addressing
  4011. <pre class=simpledef>
  4012. Name: <dfn dfn export lt="prop-team">team</dfn>
  4013. URI: https://forgefed.org/ns#team
  4014. Domain: [=Object=]
  4015. Range: [=Collection=] of actors
  4016. Functional: Yes
  4017. Inverse of: None
  4018. Description:
  4019. Specifies a [=Collection=] of actors who are working on the object, or
  4020. responsible for it, or managing or administrating it, or having edit access
  4021. to it. For example, for a [=Repository=], it could be the people who have
  4022. push/edit access, the "collaborators" of the repository.
  4023. </pre>
  4024. <div class=example>
  4025. A repository *https://dev.example/aviva/treesim*:
  4026. <xmp highlight=json-ld line-highlight=20>
  4027. {
  4028. "@context": [
  4029. "https://www.w3.org/ns/activitystreams",
  4030. "https://w3id.org/security/v1",
  4031. "https://forgefed.org/ns"
  4032. ],
  4033. "id": "https://dev.example/aviva/treesim",
  4034. "type": "Repository",
  4035. "publicKey": {
  4036. "id": "https://dev.example/aviva/treesim#main-key",
  4037. "owner": "https://dev.example/aviva/treesim",
  4038. "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
  4039. },
  4040. "inbox": "https://dev.example/aviva/treesim/inbox",
  4041. "outbox": "https://dev.example/aviva/treesim/outbox",
  4042. "followers": "https://dev.example/aviva/treesim/followers",
  4043. "name": "Tree Growth 3D Simulation",
  4044. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>",
  4045. "team": "https://dev.example/aviva/treesim/team"
  4046. }
  4047. </xmp>
  4048. The repository's team *https://dev.example/aviva/treesim/team*:
  4049. <xmp highlight=json-ld line-highlight=4>
  4050. {
  4051. "@context": "https://www.w3.org/ns/activitystreams",
  4052. "id": "https://dev.example/aviva/treesim/team",
  4053. "type": "Collection",
  4054. "totalItems": 3,
  4055. "items": [
  4056. "https://dev.example/aviva",
  4057. "https://dev.example/luke",
  4058. "https://code.community/users/lorax"
  4059. ]
  4060. }
  4061. </xmp>
  4062. </div>
  4063. ### Tracker linking
  4064. <pre class=simpledef>
  4065. Name: <dfn dfn export>ticketsTrackedBy</dfn>
  4066. URI: https://forgefed.org/ns#ticketsTrackedBy
  4067. Domain: [=Object=]
  4068. Range: [=Object=] that is an actor
  4069. Functional: Yes
  4070. Inverse of: [=tracksTicketsFor=]
  4071. Description:
  4072. Identifies the actor which tracks tickets related to the given object. This
  4073. is the actor to whom you send tickets you'd like to open against the
  4074. object.
  4075. </pre>
  4076. <div class=example>
  4077. <xmp highlight=json-ld line-highlight=10>
  4078. {
  4079. "@context": [
  4080. "https://www.w3.org/ns/activitystreams",
  4081. "https://forgefed.org/ns"
  4082. ],
  4083. "id": "https://dev.example/aviva/treesim",
  4084. "type": "Repository",
  4085. "name": "Tree Growth 3D Simulation",
  4086. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>",
  4087. "ticketsTrackedBy": "https://bugs.example/projects/treesim"
  4088. }
  4089. </xmp>
  4090. </div>
  4091. <pre class=simpledef>
  4092. Name: <dfn dfn export>tracksTicketsFor</dfn>
  4093. URI: https://forgefed.org/ns#tracksTicketsFor
  4094. Domain: [=Object=] that is an actor
  4095. Range: [=Object=]
  4096. Functional: No
  4097. Inverse of: [=ticketsTrackedBy=]
  4098. Description:
  4099. Identifies objects for which which this ticket tracker tracks tickets. When
  4100. you'd like to open a ticket against those objects, you can send them to
  4101. this tracker.
  4102. </pre>
  4103. <div class=example>
  4104. <xmp highlight=json-ld line-highlight=8>
  4105. {
  4106. "@context": [
  4107. "https://www.w3.org/ns/activitystreams",
  4108. "https://forgefed.org/ns"
  4109. ],
  4110. "id": "https://bugs.example/treesim",
  4111. "type": "Project",
  4112. "tracksTicketsFor": [
  4113. "https://dev.example/aviva/liblsystem",
  4114. "https://dev.example/aviva/3d-tree-models",
  4115. "https://dev.example/aviva/treesim"
  4116. ]
  4117. }
  4118. </xmp>
  4119. </div>
  4120. <pre class=simpledef>
  4121. Name: <dfn dfn export>sendPatchesTo</dfn>
  4122. URI: https://forgefed.org/ns#sendPatchesTo
  4123. Domain: [=Repository=]
  4124. Range: [=PatchTracker=]
  4125. Functional: Yes
  4126. Inverse of: [=tracksPatchesFor=]
  4127. Description:
  4128. Identifies the [=PatchTracker=] which tracks patches and merge requests
  4129. related to the given repository. This is the actor to whom you send patches
  4130. and merge requests you'd like to open against the repository.
  4131. </pre>
  4132. <div class=example>
  4133. <xmp highlight=json-ld line-highlight=10>
  4134. {
  4135. "@context": [
  4136. "https://www.w3.org/ns/activitystreams",
  4137. "https://forgefed.org/ns"
  4138. ],
  4139. "id": "https://dev.example/repos/treesim",
  4140. "type": "Repository",
  4141. "name": "Tree Growth 3D Simulation",
  4142. "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>",
  4143. "sendPatchesTo": "https://bugs.example/pr-trackers/treesim"
  4144. }
  4145. </xmp>
  4146. </div>
  4147. <pre class=simpledef>
  4148. Name: <dfn dfn export>tracksPatchesFor</dfn>
  4149. URI: https://forgefed.org/ns#tracksPatchesFor
  4150. Domain: [=PatchTracker=]
  4151. Range: [=Repository=]
  4152. Functional: No
  4153. Inverse of: [=sendPatchesTo=]
  4154. Description:
  4155. Identifies a repository for which which this patch and merge request
  4156. tracker tracks patches and merge requests. When you'd like to open patches
  4157. or merge requests against that repository, you can send them to this
  4158. tracker.
  4159. </pre>
  4160. <div class=example>
  4161. <xmp highlight=json-ld line-highlight=8>
  4162. {
  4163. "@context": [
  4164. "https://www.w3.org/ns/activitystreams",
  4165. "https://forgefed.org/ns"
  4166. ],
  4167. "id": "https://project.example/treesim",
  4168. "type": "PatchTracker",
  4169. "tracksPatchesFor": [
  4170. "https://dev.example/aviva/liblsystem",
  4171. "https://dev.example/aviva/3d-tree-models",
  4172. "https://dev.example/aviva/treesim"
  4173. ]
  4174. }
  4175. </xmp>
  4176. </div>
  4177. ### Repository forking
  4178. <pre class=simpledef>
  4179. Name: <dfn dfn export>forkedFrom</dfn>
  4180. URI: https://forgefed.org/ns#forkedFrom
  4181. Domain: [=Repository=]
  4182. Range: [=Repository=]
  4183. Functional: Yes
  4184. Inverse of: [=forks=]
  4185. Description:
  4186. Identifies the [=Repository=] which this [=Repository=] was created as a
  4187. fork of, i.e. by cloning it.
  4188. </pre>
  4189. <div class=example>
  4190. <xmp highlight=json-ld line-highlight=8>
  4191. {
  4192. "@context": [
  4193. "https://www.w3.org/ns/activitystreams",
  4194. "https://forgefed.org/ns"
  4195. ],
  4196. "id": "https://example.dev/alice/myfork/",
  4197. "type": "Repository",
  4198. "forkedFrom": {
  4199. "type": "Repository",
  4200. "id": "https://example.dev/luke/myrepo/"
  4201. }
  4202. }
  4203. </xmp>
  4204. </div>
  4205. <pre class=simpledef>
  4206. Name: <dfn dfn export>forks</dfn>
  4207. URI: https://forgefed.org/ns#forks
  4208. Domain: [=Repository=]
  4209. Range: [=OrderedCollection=] of items of type [=Repository=]
  4210. Functional: Yes
  4211. Inverse of: [=forkedFrom=]
  4212. Description:
  4213. Identifies an [=OrderedCollection=] of [=Repository=]s which were created
  4214. as forks of this [=Repository=], i.e. by cloning it. The order of the
  4215. collection items is by reverse chronological order of the forking events.
  4216. </pre>
  4217. <div class=example>
  4218. <xmp highlight=json-ld line-highlight=8>
  4219. {
  4220. "@context": [
  4221. "https://www.w3.org/ns/activitystreams",
  4222. "https://forgefed.org/ns"
  4223. ],
  4224. "id": "https://example.dev/luke/myrepo/",
  4225. "type": "Repository",
  4226. "forks": {
  4227. "type": "OrderedCollection",
  4228. "totalItems": 1,
  4229. "orderedItems": [
  4230. {
  4231. "id": "https://example.dev/alice/myfork/",
  4232. "type": "Repository",
  4233. }
  4234. ]
  4235. },
  4236. }
  4237. </xmp>
  4238. </div>
  4239. ### Access control
  4240. <pre class=simpledef>
  4241. Name: <dfn dfn export>fulfills</dfn>
  4242. URI: https://forgefed.org/ns#fulfills
  4243. Domain: [=Activity=]
  4244. Range: [=Activity=]
  4245. Functional: No
  4246. Inverse of: None
  4247. Description:
  4248. For an activity *A* that `fulfills` some other activity *B*, specifies that
  4249. *A* has been published as part of fulfilling the action requested by *B*.
  4250. For example, if Alice creates a new repository using `Create`, she may want
  4251. to instantly automatically start following this new repository using
  4252. `Follow` (to be notified when her friends push commits there). This
  4253. `Follow` `fulfills` the `Create`; it's an activity automatically sent as
  4254. part of creating a new repository.
  4255. </pre>
  4256. <div class=example>
  4257. <xmp highlight=json-ld line-highlight=9>
  4258. TODO
  4259. </xmp>
  4260. </div>
  4261. <pre class=simpledef>
  4262. Name: <dfn dfn export>allows</dfn>
  4263. URI: https://forgefed.org/ns#allows
  4264. Domain: [=Grant=]
  4265. Range: [=CapabilityUsage=]
  4266. Functional: No
  4267. Inverse of: None
  4268. Description:
  4269. Specifies which modes of using this [=Grant=] are being allowd by it. The
  4270. two conceptual operations that `Grant`s support are invocation (acting on
  4271. the resource under the specified role) and delegation (passing on the
  4272. access to more actors, possibly with reduced privileges). This property
  4273. specifies which of these operations are supported, and under which
  4274. conditions. See [=CapabilityUsage=] for specific values to use.
  4275. </pre>
  4276. <div class=example>
  4277. <xmp highlight=json-ld line-highlight=9>
  4278. TODO
  4279. </xmp>
  4280. </div>
  4281. <pre class=simpledef>
  4282. Name: <dfn dfn export>capability</dfn>
  4283. URI: https://forgefed.org/ns#capability
  4284. Domain: [=Activity=]
  4285. Range: [=Grant=]
  4286. Functional: Yes
  4287. Inverse of: None
  4288. Description:
  4289. Specifies a previously published [=Grant=] activity providing relevant
  4290. access permissions. For example, if Alice wants to resolve a [=Ticket=]
  4291. under some project, she will send an activity with `capability` referring
  4292. to the `Grant` activity that gave her collaborator access to that project,
  4293. which happens to include permission to resolve tickets.
  4294. </pre>
  4295. <div class=example>
  4296. <xmp highlight=json-ld line-highlight=9>
  4297. TODO
  4298. </xmp>
  4299. </div>
  4300. <pre class=simpledef>
  4301. Name: <dfn dfn export>managedBy</dfn>
  4302. URI: https://forgefed.org/ns#managedBy
  4303. Domain: [=Object=]
  4304. Range: [=Object=] that is an actor
  4305. Functional: Yes
  4306. Inverse of: None
  4307. Description:
  4308. Identifies the actor that controls the given resource, and to whom
  4309. activities asking to modify the resource may be submitted.
  4310. </pre>
  4311. <div class=example>
  4312. <xmp highlight=json-ld line-highlight=9>
  4313. {
  4314. "@context": [
  4315. "https://www.w3.org/ns/activitystreams",
  4316. "https://forgefed.org/ns"
  4317. ],
  4318. "type": "Ticket",
  4319. "id": "https://example.dev/alice/myrepo/issues/42",
  4320. "context": "https://example.dev/alice/myrepo",
  4321. "managedBy": "https://example.dev/alice/myrepo",
  4322. "attributedTo": "https://dev.community/bob",
  4323. "summary": "Nothing works!",
  4324. "content": "<p>Please fix. <i>Everything</i> is broken!</p>",
  4325. "mediaType": "text/html",
  4326. "source": {
  4327. "content": "Please fix. *Everything* is broken!",
  4328. "mediaType": "text/markdown; variant=CommonMark"
  4329. },
  4330. "isResolved": false
  4331. }
  4332. </xmp>
  4333. </div>
  4334. <pre class=simpledef>
  4335. Name: <dfn dfn export>delegates</dfn>
  4336. URI: https://forgefed.org/ns#delegates
  4337. Domain: [=Grant=]
  4338. Range: [=Grant=]
  4339. Functional: Yes
  4340. Inverse of: None
  4341. Description:
  4342. Actors can use [=Grant=] activities to allow other actors to access their
  4343. resources. They can also allow those other actors to pass on (delegate)
  4344. this access to even more actors. For a `Grant` that delegates access
  4345. provided by an earlier `Grant`, the former uses `delegates` to specify the
  4346. latter. That earlier `Grant` is also called the "parent capability" of this
  4347. `Grant`.
  4348. </pre>
  4349. <div class=example>
  4350. <xmp highlight=json-ld line-highlight=9>
  4351. TODO
  4352. </xmp>
  4353. </div>
  4354. ### Repository mirroring
  4355. <pre class=simpledef>
  4356. Name: <dfn dfn export>mirrors</dfn>
  4357. URI: https://forgefed.org/ns#mirrors
  4358. Domain: [=Repository=]
  4359. Range: [=Repository=]
  4360. Functional: Yes
  4361. Inverse of: [=mirroredBy=]
  4362. Description:
  4363. Identifies the [=Repository=] which this [=Repository=] copies content from
  4364. (i.e. what this repository is a "pull mirror" of).
  4365. </pre>
  4366. <div class=example>
  4367. <xmp highlight=json-ld line-highlight=8>
  4368. {
  4369. "@context": [
  4370. "https://www.w3.org/ns/activitystreams",
  4371. "https://forgefed.org/ns"
  4372. ],
  4373. "id": "https://example.dev/alice/mymirror/",
  4374. "type": "Repository",
  4375. "mirrors": {
  4376. "type": "Repository",
  4377. "id": "https://example.dev/luke/myrepo/"
  4378. }
  4379. }
  4380. </xmp>
  4381. </div>
  4382. <pre class=simpledef>
  4383. Name: <dfn dfn export>mirroredBy</dfn>
  4384. URI: https://forgefed.org/ns#mirroredBy
  4385. Domain: [=Repository=]
  4386. Range: [=Repository=]
  4387. Functional: No
  4388. Inverse of: [=mirrors=]
  4389. Description:
  4390. Identifies a [=Repository=] which copies content from this repository (i.e.
  4391. "pull mirror" of this repository).
  4392. </pre>
  4393. <div class=example>
  4394. <xmp highlight=json-ld line-highlight=8>
  4395. {
  4396. "@context": [
  4397. "https://www.w3.org/ns/activitystreams",
  4398. "https://forgefed.org/ns"
  4399. ],
  4400. "id": "https://example.dev/luke/myrepo/",
  4401. "type": "Repository",
  4402. "mirroredBy": {
  4403. "type": "Repository",
  4404. "id": "https://example.dev/alice/mymirror/"
  4405. }
  4406. }
  4407. </xmp>
  4408. </div>
  4409. <pre class=simpledef>
  4410. Name: <dfn dfn export>mirrorsTo</dfn>
  4411. URI: https://forgefed.org/ns#mirrorsTo
  4412. Domain: [=Repository=]
  4413. Range: [=Repository=]
  4414. Functional: No
  4415. Inverse of: [=mirroredFrom=]
  4416. Description:
  4417. Identifies a [=Repository=] which this repository copies content to (i.e.
  4418. "push mirror" of this repository)
  4419. </pre>
  4420. <div class=example>
  4421. <xmp highlight=json-ld line-highlight=8>
  4422. {
  4423. "@context": [
  4424. "https://www.w3.org/ns/activitystreams",
  4425. "https://forgefed.org/ns"
  4426. ],
  4427. "id": "https://example.dev/alice/myrepo/",
  4428. "type": "Repository",
  4429. "mirrorsTo": {
  4430. "type": "Repository",
  4431. "id": "https://example.dev/alice-backup/myrepo/"
  4432. }
  4433. }
  4434. </xmp>
  4435. </div>
  4436. <pre class=simpledef>
  4437. Name: <dfn dfn export>mirroredFrom</dfn>
  4438. URI: https://forgefed.org/ns#mirroredFrom
  4439. Domain: [=Repository=]
  4440. Range: [=Repository=]
  4441. Functional: Yes
  4442. Inverse of: [=mirrorsTo=]
  4443. Description:
  4444. Identifies the [=Repository=] which copies its content to this
  4445. [=Repository=] (ie. what this repository is a "push mirror" of).
  4446. </pre>
  4447. <div class=example>
  4448. <xmp highlight=json-ld line-highlight=8>
  4449. {
  4450. "@context": [
  4451. "https://www.w3.org/ns/activitystreams",
  4452. "https://forgefed.org/ns"
  4453. ],
  4454. "id": "https://example.dev/alice-backup/myrepo/",
  4455. "type": "Repository",
  4456. "mirroredFrom": {
  4457. "type": "Repository",
  4458. "id": "https://example.dev/alice/myrepo/"
  4459. }
  4460. }
  4461. </xmp>
  4462. </div>
  4463. ## Values
  4464. ### Capability uses
  4465. <pre class=simpledef>
  4466. Name: <dfn dfn export>gatherAndConvey</dfn>
  4467. URI: https://forgefed.org/ns#gatherAndConvey
  4468. Type: [=CapabilityUsage=]
  4469. Conditions:
  4470. <ul>
  4471. <li>
  4472. The `Grant`'s [=target=] MUST be a [=Project=]
  4473. </li>
  4474. <li>
  4475. It may delegate the `Grant`, allowing only `gatherAndConvey`, to
  4476. parent projects
  4477. </li>
  4478. <li>
  4479. It may delegate the `Grant`, allowing only `distribute`, to teams
  4480. to which it allows to access it
  4481. </li>
  4482. <li>
  4483. It may delegate the `Grant`, allowing `invoke` only, to people to
  4484. which it allows to access it
  4485. </li>
  4486. </ul>
  4487. </pre>
  4488. <pre class=simpledef>
  4489. Name: <dfn dfn export>distribute</dfn>
  4490. URI: https://forgefed.org/ns#distribute
  4491. Type: [=CapabilityUsage=]
  4492. Conditions:
  4493. <ul>
  4494. <li>
  4495. The `Grant`'s [=target=] MUST be a [=Team=]
  4496. </li>
  4497. <li>
  4498. It may delegate the `Grant`, allowing `distribute` only, to its
  4499. subteams
  4500. </li>
  4501. <li>
  4502. It may delegate the `Grant`, allowing `invoke` only, to its members
  4503. </li>
  4504. </ul>
  4505. </pre>
  4506. <pre class=simpledef>
  4507. Name: <dfn dfn export>invoke</dfn>
  4508. URI: https://forgefed.org/ns#invoke
  4509. Type: [=CapabilityUsage=]
  4510. Conditions:
  4511. <ul>
  4512. <li>
  4513. The `Grant`'s [=target=] may invoke it, i.e. use it as the
  4514. [=capability=] in another activity, that requests to access or
  4515. modify the resource specified by the `Grant`'s [=context=]
  4516. </li>
  4517. </ul>
  4518. </pre>
  4519. ### Roles
  4520. <pre class=simpledef>
  4521. Name: <dfn dfn export>visit</dfn>
  4522. URI: https://forgefed.org/ns#visit
  4523. Type: [=Role=]
  4524. Description:
  4525. Authorizes the [=Grant=] recipient (i.e. [=target=]) to view the [=Grant=]
  4526. resource (i.e. [=context=]), which includes retrieving objects via HTTP and
  4527. pulling/cloning VCS repos
  4528. </pre>
  4529. <pre class=simpledef>
  4530. Name: <dfn dfn export>report</dfn>
  4531. URI: https://forgefed.org/ns#report
  4532. Type: [=Role=]
  4533. Description:
  4534. Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
  4535. resource (i.e. [=context=]) anything that the [=visit=] role authorizes,
  4536. and also to do basic community participation tasks:
  4537. [Open an issue](#opening-issue),
  4538. [submit a PR](#opening-mr),
  4539. [create comments](#commenting)
  4540. and discussion threads,
  4541. edit public wikis,
  4542. submit PR reviews.
  4543. </pre>
  4544. <pre class=simpledef>
  4545. Name: <dfn dfn export>triage</dfn>
  4546. URI: https://forgefed.org/ns#triage
  4547. Type: [=Role=]
  4548. Description:
  4549. Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
  4550. resource (i.e. [=context=]) anything that the [=report=] role authorizes,
  4551. and also to edit issue/PR propeties (labels, milestones, due dates, etc.),
  4552. close and reopen issues and PRs, assign and unassign people to issues and
  4553. PRs, request PR reviews, hide disruptive comments (a moderation action),
  4554. lock and move discussions.
  4555. </pre>
  4556. <pre class=simpledef>
  4557. Name: <dfn dfn export>write</dfn>
  4558. URI: https://forgefed.org/ns#write
  4559. Type: [=Role=]
  4560. Description:
  4561. Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
  4562. resource (i.e. [=context=]) anything that the [=triage=] role authorizes,
  4563. and also to
  4564. apply PR suggested changes,
  4565. edit non-public wikis,
  4566. create/edit/delete labels,
  4567. merge a PR,
  4568. [push to VCS repositories](#pushing),
  4569. create/edit/run/cancel CI recipes,
  4570. manage releases,
  4571. publish packages,
  4572. create web IDE coding sessions.
  4573. </pre>
  4574. <pre class=simpledef>
  4575. Name: <dfn dfn export>maintain</dfn>
  4576. URI: https://forgefed.org/ns#maintain
  4577. Type: [=Role=]
  4578. Description:
  4579. Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
  4580. resource (i.e. [=context=]) anything that the [=write=] role authorizes,
  4581. and also to edit project and component descriptions and settings unrelated
  4582. to access, enable/disable components, configure "Pages" publishing of
  4583. static websites from repos, push to repos' protected branches.
  4584. </pre>
  4585. <pre class=simpledef>
  4586. Name: <dfn dfn export>admin</dfn>
  4587. URI: https://forgefed.org/ns#admin
  4588. Type: [=Role=]
  4589. Description:
  4590. Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
  4591. resource (i.e. [=context=]) anything that the [=maintain=] role authorizes,
  4592. and also to
  4593. [manage access to projects, components and teams](#managing-access),
  4594. merge PRs even without reviews,
  4595. delete issues,
  4596. change project/component/team visibility,
  4597. edit project/component/team access-related settings,
  4598. change a repo's default branch,
  4599. manage webhooks and deployment,
  4600. move components and projects between projects,
  4601. archive projects/components,
  4602. delete components/projects/teams.
  4603. </pre>
  4604. <pre class=simpledef>
  4605. Name: <dfn dfn export>delegate</dfn>
  4606. URI: https://forgefed.org/ns#delegate
  4607. Type: [=Role=]
  4608. Description:
  4609. Authorizes the [=Grant=] recipient (i.e. [=target=]) to send access
  4610. delegations to the [=Grant=] sender (i.e. [=actor=])
  4611. </pre>
  4612. <pre class=biblio>
  4613. {
  4614. "wikipedia-ticket-tracker": {
  4615. "href": "https://en.wikipedia.org/wiki/Issue_tracking_system",
  4616. "title": "Wikipedia: Issue tracking system"
  4617. },
  4618. "wikipedia-pr": {
  4619. "href": "https://en.wikipedia.org/wiki/Distributed_version_control#Pull_requests",
  4620. "title": "Wikipedia: Pull requests"
  4621. },
  4622. "fep-8b32": {
  4623. "href": "https://codeberg.org/fediverse/fep/src/branch/main/feps/fep-8b32.md",
  4624. "title": "FEP-8b32: Object Integrity Proofs"
  4625. }
  4626. }
  4627. </pre>
  4628. <pre class=anchors>
  4629. urlPrefix: https://www.w3.org/TR/xmlschema11-2/#; type: dfn; spec: xmlschema11-2
  4630. text: dateTime
  4631. urlPrefix: http://purl.org/dc/terms/; type: dfn; spec: dcterms
  4632. text: created
  4633. urlPrefix: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-; type: dfn; spec: activitystreams-vocabulary
  4634. text: Accept
  4635. text: Add
  4636. text: Create
  4637. text: Invite
  4638. text: Join
  4639. text: Leave
  4640. text: Offer
  4641. text: Reject
  4642. text: Remove
  4643. text: Undo
  4644. urlPrefix: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-; type: dfn; spec: activitystreams-vocabulary
  4645. text: Activity
  4646. text: Collection
  4647. text: Image
  4648. text: Link
  4649. text: Note
  4650. text: Object
  4651. text: OrderedCollection
  4652. text: Person
  4653. urlPrefix: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-; type: dfn; spec: activitystreams-vocabulary
  4654. text: actor
  4655. text: attachment
  4656. text: attributedTo
  4657. text: content
  4658. text: context
  4659. text: duration
  4660. text: endTime
  4661. text: id
  4662. text: inbox
  4663. text: inReplyTo
  4664. text: instrument
  4665. text: items
  4666. text: mediaType
  4667. text: name
  4668. text: object
  4669. text: orderedItems
  4670. text: origin
  4671. text: published
  4672. text: relationship
  4673. text: replies
  4674. text: result
  4675. text: source
  4676. text: startTime
  4677. text: subject
  4678. text: summary
  4679. text: tag
  4680. text: target
  4681. text: to
  4682. text: type
  4683. urlPrefix: https://www.w3.org/TR/activitypub/#; type: dfn; spec: activitypub
  4684. text: followers
  4685. </pre>