12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306 |
- <pre class='metadata'>
- Status: LS-BRANCH
- Group: ForgeFed Working Group
- Editor: Alice Doe, MyCoolCompany https://mycoolcompany.com/home, https://alicedoe.me/about
- Editor: Bob Doe, SomeOrg https://some.org, https://home.net/bob
- Favicon: /img/favicon.png
- Logo: /img/logo.svg
- Status Text: Work in progress
- Issue Tracking: Codeberg https://codeberg.org/forgefed/forgefed/issues
- Issue Tracker Template: https://codeberg.org/forgefed/forgefed/issues/{0}
- Indent: 4
- Work Status: exploring
- Repository: https://codeberg.org/forgefed/forgefed forgefed
- Line Numbers: yes
- Markup Shorthands: markdown yes, css no, http no, idl no, markup no
- Title: ForgeFed
- Shortname: forgefed
- URL: https://forgefed.org/spec
- Revision: 1
- Abstract:
- This document describes the ForgeFed vocabulary. It's intended to be an
- extension of [[ActivityStreams-Vocabulary]] and provides additional
- vocabulary for federation of project management and version control system
- hosting and collaboration platforms.
- This document describes the rules and guidelines for representing version
- control and project management related objects as linked data, using the
- ForgeFed vocabulary, ActivityStreams 2, and other related vocabularies.
- This document provides instructions for using ActivityPub activities and
- properties to represent forge events, and describes the side-effects these
- activities should have.
- </pre>
- Issue: (fr33) In the abstract I just pasted the 3 abstracts of the separate
- specs. Write something new, probably with inspiration from existing specs such
- as ActivityPub.
- This draft is generated from branch <code>[GITBRANCH]</code>, commit
- <a href="https://codeberg.org/ForgeFed/ForgeFed/commit/[GITCOMMIT]">[GITSHORT]</a>.
- # Introduction # {#intro}
- Note: The spec is still under construction. Want to start implementing? Check
- out the ActivityPub implementation guide, and then the [[#s2s]] section here.
- Issue: Below are the 3 intro texts from the 3 specs. We probably want to
- replace them with a human-friendly tutorial-like example, like in the
- ActivityPub spec.
- The ForgeFed Vocabulary describes a set of types and properties to be used by
- platforms that support the ForgeFed protocol. This specification describes only
- the new vocabulary called ForgeFed. The ForgeFed behavior specification
- describes how to use this vocabulary, along with standard ActivityPub
- vocabulary, to support the ForgeFed protocol.
- **The ForgeFed modeling specification** is a set of rules and guidelines which
- describe version control repository and project management related objects and
- properties, and specify how to represent them as JSON-LD objects (and linked
- data in general) using the ForgeFed vocabulary and related vocabularies and
- ontologies. Using these modeling rules consistently across implementations and
- instances allows to have a common language spoken across networks of software
- forges, project management apps and more.
- The ForgeFed vocabulary specification defines a dedicated vocabulary of
- forge-related terms, and the **modeling specification** uses these terms, along
- with terms that already exist in ActivityPub or elsewhere and can be reused for
- forge federation.
- The ForgeFed behavior specification provides instructions for using Activities,
- and which Activities and properties to use, to represent forge events, and
- describes the side-effects these Activities should have. The objects used as
- inputs and outputs of behavior descriptions there are defined here in the
- **modeling specification**.
- **The ForgeFed behavior specification** is a set of instructions for
- representing version control systems and project management related transactions
- using ActivityPub activity objects, and it describes the side effects and
- expected results of sending and receiving these activities. The vocabulary for
- these activities includes standard ActivityPub terms, new terms defined by
- ForgeFed, and terms borrowed from other external vocabularies.
- The ForgeFed vocabulary specification defines a dedicated vocabulary of
- forge-related terms, and the **behavior specification** uses these terms, along
- with terms that already exist in ActivityPub or elsewhere and can be reused for
- forge federation.
- The ForgeFed modeling specification defines rules for representing forge
- related objects as ActivityPub JSON-LD objects, and these objects are used in
- the **behavior specification**, included in activities, mentioned in
- activities, or modified as a result of activity side-effects.
- # Objects
- ## Kinds of Objects
- Objects are the core concept around which both ActivityPub and ForgeFed are
- built. Examples of Objects are [=Note=], [=Ticket=], [=Image=],
- [=Create=], [=Push=]. Some objects are resources, which are objects that
- contain or represent information and user made or program made content, and
- some objects are helpers that exist as implementation detail aren't necessarily
- exposed to humans or are useful to humans. But everything is an [=Object=],
- represented as compacted JSON-LD.
- ForgeFed is an ActivityPub extension, and communication between ForgeFed
- implementations occurs using activity objects sent to actor inboxes and
- outboxes.
- There are 4 kinds of objects in ForgeFed:
- : Activities
- :: These are objects that describe actions, such as actions that
- happened, actions that are happening, or a request to perform an action.
- Their primary use is for server-to-server interaction between actors by
- being sent to an actor's inbox, and client-to-server interaction between a
- person or program and an actor they control by being sent to the actor's
- outbox. Activities can also appear or be linked inside other objects and
- activities and be listed in Collections.
- : Actors
- :: These are static persistent objects that have an [=inbox=] and can be
- directly interacted with by POSTing activities to it. Their primary use is
- to contain or represent information and output of user actions or program
- actions, and to manage access to this information and modifications of it.
- : Child objects
- :: These are persistent objects that, like actors, contain or
- represent information and output of user actions or program actions, but
- they do not have their own [=inbox=] and are not directly interacted with.
- A managed static object always has a parent object, which is an actor, and
- that actor's inbox is the way to interact with the child object. The parent
- actor manages access and modification of the child object.
- : Global helper objects
- :: These are objects that do not belong to any actor and do not need any
- interaction through activities. As such, they do not exactly fit into the
- actor model, but may be involved in implementation details and practical
- considerations.
- Actors, children, and globals are referred to in ForgeFed as *static* objects,
- while activities are *dynamic* objects. The terms *constant* and *variable*
- are used to indicate whether an object changes during its lifetime or not.
- *Static* objects, in addition to being an actor or child or global, also have a
- resource/helper distinction:
- : Resource
- :: Contains or represents information and user made or program made
- content, usually belongs to the domain model of version control systems and
- project management.
- : Helper
- :: Used for running things behind the scenes, not exposed directly as
- user content, may be transient or auto generated, usually related to
- implementation detail and not to concepts of version control and project
- management.
- ## Object Publishing and Hosting ## {#publishing}
- In ForgeFed, actors host their child objects locally, meaning the actor and the
- child object are hosted on the same instance. Actors may create remote objects by
- *offering* them to the relevant actor, which then may create the object on their
- side and assign it a URI.
- The process begins with an [=Offer=] activity, in which:
- - [=object=] MUST be the object being offered for publishing, and that object
- MUST NOT have an [=id=]
- - [=target=] MUST indicate under which list/collection/context the sender would
- like the object to be published (it may also be the URI of the target actor
- itself)
- Among the recipients listed in the [=Offer=]'s recipient fields, exactly one
- recipient is the actor who is responsible for inspecting and possibly publishing
- the newly created object, and possibly sending back an [=Accept=] or a [=Reject=].
- We'll refer to this actor as the *target actor*. Specific object types described
- throughout this specification have a specific meaning for the *target actor*,
- which processing and inspection it is expected to do, and where it is expected
- to list the URI of the object once it publishes it.
- The sender is essentially asking that the target actor hosts the object as a
- child object and assigns is a URI, allowing to observe and interact with the
- object. The target actor will be responsible for hosting and controlling the
- object, and the sender will just be mentioned as the author.
- When an actor *A* receives the [=Offer=] activity, they can determine whether
- they're the *target actor* as follows: If the [=target=] is *A* or a child
- object of *A*, then *A* is the *target actor*. Otherwise, *A* isn't the target
- actor.
- In the following example, Luke wants to open a ticket under Aviva's Game Of
- Life simulation app:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://forge.example/luke/outbox/02Ljp",
- "type": "Offer",
- "actor": "https://forge.example/luke",
- "to": [
- "https://dev.example/aviva/game-of-life",
- "https://dev.example/aviva/game-of-life/team",
- "https://dev.example/aviva/game-of-life/followers"
- ],
- "object": {
- "type": "Ticket",
- "attributedTo": "https://forge.example/luke",
- "summary": "Test test test",
- "content": "<p>Just testing</p>",
- "mediaType": "text/html",
- "source": {
- "mediaType": "text/markdown; variant=Commonmark",
- "content": "Just testing"
- }
- },
- "target": "https://dev.example/aviva/game-of-life"
- }
- </xmp>
- </div>
- The *target actor* SHOULD send an [=Accept=] or a [=Reject=] activity to the
- Offer's author in response. If the *target actor* sends an Accept, it MUST
- host its own copy, assigning an [=id=] to the newly published object and adding
- it to the expected list specified by the [=Offer=]'s [=target=].
- If the *target actor* sends a [=Reject=], it MUST NOT add the object's [=id=]
- to that list. However if the *target actor* doesn't make any use of the
- object, it MAY choose not to send a Reject, e.g. to protect user privacy. The
- `Accept` or `Reject` may also be delayed, e.g. until review by a human user;
- that is implementation dependent, and implementations should not rely on an
- instant response.
- In the [=Accept=] activity:
- - [=object=] MUST be the Offer activity or its [=id=]
- - [=result=] MUST be specified and be the [=id=] of the new child object now
- hosted by the *target actor*, which is extracted from the [=Offer=]'s
- [=object=]
- In the following example, Luke's ticket is opened automatically and Aviva's
- Game Of Life repository, which is an actor, automatically sends Luke an Accept
- activity:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/game-of-life/outbox/096al",
- "type": "Accept",
- "actor": "https://dev.example/aviva/game-of-life",
- "to": [
- "https://forge.example/luke",
- "https://dev.example/aviva/game-of-life/team",
- "https://dev.example/aviva/game-of-life/followers"
- ],
- "object": "https://forge.example/luke/outbox/02Ljp",
- "result": "https://dev.example/aviva/game-of-life/issues/113"
- }
- </xmp>
- </div>
- # Server to Server Interactions # {#s2s}
- Issue: This section describes the whole flow of actor interactions, that allows
- the federated implementation of the various features of forges and related
- software. It provides a complete picture of interaction flows, which the actor
- API section can't, because it focuses on a single actor type at a time.
- ## Reporting Pushed Commits ## {#pushing}
- - Role required for pushing: [=write=]
- - Role required for reporting a Push: None (because the [=Repository=] itself
- is publishing the Push)
- The ForgeFed [=Push=] activity can be used for representing an action
- of pushing commits into a [=Repository=]. Two actors are
- involved in the process, the *pusher* (usually a person) and the *repository*,
- and they may be hosted on different instances.
- [=Push=] activities MUST be authored and published by the [=Repository=], not
- by the actor that pushed. That actor is specified in the Push's
- [=attributedTo=] property.
- Upon a successful push, a ForgeFed implementation that publishes a Push
- activity MUST provide the [=type=], [=actor=], [=attributedTo=] and [=target=]
- properties as described in [[#Push]].
- See example in [[#Push]].
- ## Opening an issue ## {#opening-issue}
- Minimal required role: [=report=]
- The first step for opening a ticket is to determine to which actor to send the
- ticket. We'll refer to this actor as the *ticket tracker*. Given an object
- *obj* against which you'd like to open a ticket (e.g. some application's source
- code repository), look at the [=ticketsTrackedBy=]
- property of *obj*.
- - If `ticketsTrackedBy` isn't specified, then *obj* does't declare a way to
- open tickets via ForgeFed.
- - If `ticketsTrackedBy` is specified and is set to the [=id=] of *obj*
- itself, that means *obj* manages its own tickets, i.e. it is the *ticket
- tracker* to which you'll send the ticket.
- - If `ticketsTrackedBy` is specified and is set to some other object, look at
- the [=tracksTicketsFor=] property of that other object. If the [=id=] of
- *obj* is listed there under `tracksTicketsFor`, then that other object is
- the *ticket tracker* to which you'll send the ticket. Implementations
- SHOULD verify this bidirectional reference between the object and the
- tracker, and SHOULD NOT send a ticket if the bidirectional reference isn't
- found.
- Now that we've determined the *ticket tracker*, i.e. the actor to whom we'll
- send the [=Ticket=], the ticket may be opened using an [=Offer=]
- activity in which:
- - [=object=] is the ticket to be opened, it's a [=Ticket=] object with fields
- as described in [[#Ticket]]. It MUST specify at least [=attributedTo=],
- [=summary=] and [=content=], and MUST NOT specify [=id=]. If it specifies a
- [=context=], then it MUST be identical the Offer's [=target=] described
- below.
- - [=target=] is the ticket tracker to which the actor is offering the Ticket
- (e.g. a repository or project etc. under which the ticket will be opened if
- accepted). It MUST be either an actor or a child object. If it's a child
- object, the actor to whom the child object belongs MUST be listed as a
- recipient in the Offer's [=to=] field. If it's an actor, then that actor
- MUST be listed in the `to` field.
- The *target actor* MAY then send back an Accept or Reject. The action that has
- been taken by the *target actor* is indicated to the ticket author as follows:
- - If a [=Reject=] was sent, it means the ticket hasn't been assigned an
- [=id=] URI by the tracker and isn't being tracked by the tracker
- - If an [=Accept=] was sent, it means the ticket is now tracked and hosted on
- the target's side
- In the following example, Luke wants to open a ticket under Aviva's Game Of
- Life simulation app:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://forge.example/luke/outbox/02Ljp",
- "type": "Offer",
- "actor": "https://forge.example/luke",
- "to": [
- "https://dev.example/aviva/game-of-life",
- "https://dev.example/aviva/game-of-life/team",
- "https://dev.example/aviva/game-of-life/followers"
- ],
- "object": {
- "type": "Ticket",
- "attributedTo": "https://forge.example/luke",
- "summary": "Test test test",
- "content": "<p>Just testing</p>",
- "mediaType": "text/html",
- "source": {
- "mediaType": "text/markdown; variant=Commonmark",
- "content": "Just testing"
- }
- },
- "target": "https://dev.example/aviva/game-of-life"
- }
- </xmp>
- </div>
- Luke's ticket is opened automatically and Aviva's Game Of Life repository,
- which is an actor, automatically sends Luke an Accept activity:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/game-of-life/outbox/096al",
- "type": "Accept",
- "actor": "https://dev.example/aviva/game-of-life",
- "to": [
- "https://forge.example/luke",
- "https://dev.example/aviva/game-of-life/team",
- "https://dev.example/aviva/game-of-life/followers"
- ],
- "object": "https://forge.example/luke/outbox/02Ljp",
- "result": "https://dev.example/aviva/game-of-life/issues/113"
- }
- </xmp>
- </div>
- ## Opening a merge request ## {#opening-mr}
- Minimal required role: [=report=]
- If actor *A* wishes to submit a Merge Request (MR)/Pull Request (PR)/patch
- against a [=Repository=] *R*, it may do so by following these
- steps:
- 1. Look at *R*'s [=sendPatchesTo=] property: That is the [=PatchTracker=] to
- which the MR needs to be submitted; let's call it *P*
- 2. Verify that *P* consents to handling MRs for repository *R* by verifying
- that *R* is listed in *P*'s [=tracksPatchesFor=] property
- 3. Publish and deliver, at least to *P*, an [=Offer=] activity in which:
- - [=actor=] is *A*
- - [=target=] is *P*
- - [=object=] is a [=Ticket=] in which:
- * [=id=] isn't specified
- * [=type=] is [=Ticket=]
- * [=attributedTo=] is *A*
- * [=summary=] is a one-line HTML-escaped plain-text title of the MR
- * [=source=] is the MR's description
- * [=content=] is an HTML rendering of the MR's description
- * [=context=], if specified, is *P*
- * Among the [=attachment=]s there's exactly one of type [=Offer=], in
- which:
- + [=type=] is [=Offer=]
- + [=origin=] is the [=Repository=] or [=Branch=] from which the
- proposed changes are proposed to be merged into the target
- repository/branch
- + [=target=] is the [=Repository=] or [=Branch=] into which the
- changes are proposed to be merged
- + [=object=] is an [=OrderedCollection=] of [=Patch=] objects in
- reverse chronological order, all of them with:
- - the same [=mediaType=]
- - that [=mediaType=] MUST match the Version Control System of
- the target [=Repository=]
- - [=attributedTo=] MUST be *A*
- + At least [=origin=] or [=object=] MUST be provided, both MAY be
- provided
- Actor *P* MAY send back an [=Accept=] or [=Reject=]. The action that has been
- taken by *P* is indicated to actor *A* as follows:
- - If a [=Reject=] was sent, it mean the MR has been rejected, and isn't being
- tracked by *P*
- - If an [=Accept=] was sent, it means the MR is now tracked by *P*, and its
- [=id=] is indicated by the [=Accept=]'s [=result=]
- In the following example, Luke wants to open a Merge Request against a Game Of
- Life simulation app:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://forge.example/luke/outbox/uCSW6urN",
- "type": "Offer",
- "actor": "https://forge.example/luke",
- "to": [
- "https://dev.example/projects/game-of-life/pr-tracker"
- ],
- "cc": [
- "https://dev.example/projects/game-of-life",
- "https://dev.example/projects/game-of-life/followers",
- "https://dev.example/projects/game-of-life/repo",
- "https://dev.example/projects/game-of-life/repo/followers",
- "https://dev.example/projects/game-of-life/pr-tracker/followers"
- ],
- "object": {
- "type": "Ticket",
- "attributedTo": "https://forge.example/luke",
- "summary": "Fix the animation bug",
- "content": "<p>Please review, thanks!</p>",
- "mediaType": "text/html",
- "source": {
- "mediaType": "text/markdown; variant=Commonmark",
- "content": "Please review, thanks!"
- },
- "attachment": {
- "type": "Offer",
- "origin": {
- "type": "Branch",
- "context": "https://forge.example/luke/game-of-life",
- "ref": "refs/heads/fix-animation-bug"
- },
- "target": {
- "type": "Branch",
- "context": "https://dev.example/projects/game-of-life/repo",
- "ref": "refs/heads/main"
- },
- "object": {
- "type": "OrderedCollection",
- "totalItems": 1,
- "items": [
- {
- "type": "Patch",
- "attributedTo": "https://forge.example/luke",
- "mediaType": "application/x-git-patch",
- "content": "From c9ae5f4ff4a330b6e1196ceb7db1665bd4c1..."
- }
- ]
- }
- }
- },
- "target": "https://dev.example/projects/game-of-life/pr-tracker"
- }
- </xmp>
- </div>
- Luke's MR is opened automatically and the [=PatchTracker=]
- sends Luke an Accept activity:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/projects/game-of-life/pr-tracker/outbox/qQfFKwJ8",
- "type": "Accept",
- "actor": "https://dev.example/projects/game-of-life/pr-tracker",
- "to": [
- "https://forge.example/luke"
- ],
- "cc": [
- "https://dev.example/projects/game-of-life",
- "https://dev.example/projects/game-of-life/followers",
- "https://dev.example/projects/game-of-life/repo",
- "https://dev.example/projects/game-of-life/repo/followers",
- "https://dev.example/projects/game-of-life/pr-tracker/followers"
- ],
- "object": "https://forge.example/luke/outbox/uCSW6urN",
- "result": "https://dev.example/projects/game-of-life/pr-tracker/pulls/1219"
- }
- </xmp>
- </div>
- ## Commenting ## {#commenting}
- Minimal required role: [=report=]
- A comment on a ForgeFed resource object (such as tickets, merge requests) MUST
- be published as a [=Create=] activity, in which [=object=] is a [=Note=] with
- fields as described in [[#Comment]].
- In the following example, Luke replies to Aviva's comment under a merge request
- he submitted earlier against her Game Of Life simulation app repository:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": "https://www.w3.org/ns/activitystreams",
- "id": "https://forge.example/luke/outbox/rLaYo",
- "type": "Create",
- "actor": "https://forge.example/luke",
- "to": [
- "https://forge.example/luke/followers",
- "https://dev.example/aviva/game-of-life",
- "https://dev.example/aviva/game-of-life/followers",
- "https://dev.example/aviva/game-of-life/team",
- "https://dev.example/aviva/game-of-life/merge-requests/19/followers",
- "https://dev.example/aviva/game-of-life/merge-requests/19/team"
- ],
- "object": {
- "id": "https://forge.example/luke/comments/rD05r",
- "type": "Note",
- "attributedTo": "https://forge.example/luke",
- "to": [
- "https://forge.example/luke/followers",
- "https://dev.example/aviva/game-of-life",
- "https://dev.example/aviva/game-of-life/followers",
- "https://dev.example/aviva/game-of-life/team",
- "https://dev.example/aviva/game-of-life/merge-requests/19/followers",
- "https://dev.example/aviva/game-of-life/merge-requests/19/team"
- ],
- "context": "https://dev.example/aviva/game-of-life/merge-requests/19",
- "inReplyTo": "https://dev.example/aviva/comments/E9AGE",
- "mediaType": "text/html",
- "content": "<p>Thank you for the review! I'll submit a correction ASAP</p>",
- "source": {
- "mediaType": "text/markdown; variant=Commonmark",
- "content": "Thank you for the review! I'll submit a correction ASAP"
- },
- "published": "2019-11-06T20:49:05.604488Z"
- }
- }
- </xmp>
- </div>
- ## Granting access to shared resources ## {#managing-access}
- Minimal required role: [=admin=]
- An actor that wishes to give other specific actors access to view or modify it
- (or a child object of it), SHOULD do so according to the following
- instructions.
- ### Object capabilities
- #### Introduction #### {#s2s-grant-simple}
- An Object Capability (or in short OCap or OCAP) is a token providing access to
- certain operations on a certain resource. An actor wishing to act on a resource
- provides the token to the resource, alongside the Activity they wish to
- perform. The resource verifies the token, and if and only if it finds the token
- valid, and access to the requested Activity is allowed by the token, *then* the
- resource allows the Activity to be performed.
- The token provided by the actor to the resource, i.e. the OCAP, is the ID URI
- of a previously published [=Grant=] activity.
- The fundamental steps for accessing shared resources using OCAPs are:
- 1. The actor managing the resource (which may be the resource itself) sends a
- `Grant` activity to the actor to whom it wishes to grant access
- 2. When the actor who received the access wishes to operate on the resource,
- it sends the activity to the actor managing the resource, along with the ID
- URI of the `Grant` sent in step 1
- 3. The actor managing the resource verifies the access provided by the `Grant`
- whose ID URI is provided, and allows the activity to be performed only if
- the verification passes
- Providing the `Grant` ID URI like that when requesting to interact with a
- resource is called an *invocation* of the `Grant`. There is another operation
- possible with a `Grant` though: An actor can *delegate* a `Grant` it has
- received, i.e. pass on the access, giving it to more actors. Delegation is
- covered in a [later section](#s2s-grant-flow); for now let's assume `Grant`s
- are used only for invocation. We therefore get the following simplified
- validation process.
- When an actor *R* receives from actor *A* a request to access/modify a resource
- *r*, where the request is expressed as an activity *a* whose
- [=capability=] field specifies some other activity *g*, then *R*
- can validate *a* (i.e. decide whether or not to perform the requested action)
- using the following steps:
- 1. Resource *r* MUST be a resource that *R* manages (it may be *R* itself)
- 2. *g*'s [=type=] MUST be [=Grant=]
- 3. *g*'s [=context=] MUST be *r*
- 4. *g*'s [=target=] MUST be *A*
- 5. Verify that *g*'s [=startTime=] <= now < *g*'s [=endTime=]
- 6. Verify that *g* doesn't specify [=delegates=]
- 7. *g*'s [=actor=] MUST be *R*
- 8. Verify that *R* indeed published *g* and considers it an active grant
- (i.e. *R* hasn't disabled/revoked it)
- 9. *checkLeaf(g):*
- 1. *g*'s [=allows=] MUST be [=invoke=]
- 2. Actor *A* SHOULD be of a [=type=] to which *R* allows to perform
- activity *a* on resource *r*, i.e. *A* should probably be a [=Person=],
- or some automated service/bot
- 10. Verify that the action being requested by activity *a* to perform on
- resource *r* is within what *R* permits for the [=Role=] specified by *g*'s
- [=object=]
- At this point, activity *a* is considered authorized, and the requested action
- may be performed.
- #### Direct Granting
- When an actor *R*, managing some resource *r*, wishes to allow some other actor
- *A* to interact with *r*, under the conditions and permissions specified by
- [=Role=] *p*, then actor *R* can send to actor *A* a [=Grant=] activity
- with the following properties:
- - [=actor=]: Specifies actor *R*
- - [=context=]: Specifies resource *r*
- - [=target=]: Specifies actor *A*
- - [=object=]: Specifies role *p*
- - [=startTime=]: (optional) The time at which the Grant becomes valid
- - [=endTime=]: (recommended) The time at which the Grant expires
- - [=allows=]: Specifies [=invoke=]
- - [=delegates=]: Not used
- #### Granting the delegate role #### {#grant-delegate}
- A special case of direct granting is *granting permission to delegate*: If role
- *p* is [=delegate=], then the `Grant` [=actor=] is allowing the
- [=target=] to delegate `Grant`s to the [=actor=], i.e. to send `Grant`s meant
- for delegation or `Grants` that are themselves delegations of other `Grant`s
- (either start a chain, or extend a chain that some other actor started). More
- on delegation in the next sections.
- When an actor *A* wishes to allow some other actor *R* to delegate `Grant`s to
- actor *A*, then actor *A* can send to actor *R* a [=Grant=] activity
- with the following properties:
- - [=actor=]: Specifies actor *A*
- - [=context=]: Specifies actor *A*
- - [=target=]: Specifies actor *R*
- - [=object=]: Specifies [=delegate=]
- - [=startTime=]: (optional) The time at which the Grant becomes valid
- - [=endTime=]: (recommended) The time at which the Grant expires
- - [=allows=]: Specifies [=invoke=]
- - [=delegates=]: Not used
- #### Starting a delegation chain #### {#start-grant-chain}
- When an actor *R*, managing some resource *r*, wishes to allow or request some
- other actor *A* to delegate some access-to-*r*-under-role-*p* to certain (or
- any) other actors that *A* knows, then actor *R* can send to actor *A* a
- [=Grant=] activity with the following properties:
- - [=actor=]: Specifies actor *R*
- - [=context=]: Specifies resource *r*
- - [=target=]: Specifies actor *A*
- - [=object=]: Specifies role *p*
- - [=startTime=]: (optional) The time at which the Grant becomes valid
- - [=endTime=]: (recommended) The time at which the Grant expires
- - [=allows=]: Specifies the conditions under which actor *A* may
- delegate this `Grant` (i.e. conditions under which actor *R* will consider
- the delegation valid when verifying the chain), and what the recipients of
- the delegtaions that *A* will send (which are themselves `Grant` activites)
- are allowed to do with these `Grants` (invoke? further delegate to certain
- other actors?)
- - [=delegates=]: Not used
- - [=capability=]: *(optional)* Specifies a
- [[#grant-delegate|delegate Grant]] previously given by *A* to *R*
- The following cases are supported in ForgeFed for starting a delegation chain.
- The term 'component' used below refers to a forge related service actor. This
- may be a service of a [=type=] defined in ForgeFed (such as
- [=Repository=], [=TicketTracker=],
- [=PatchTracker=]), or a service defined in some extension.
- 1. [=actor=] is a component, [=target=] is a [=Project=]
- - Scenario: A component delegates access-to-a-resource-it-manages (which is
- often simply itself) to a project to which the component belongs
- - [=allows=] value to use: [=gatherAndConvey=]
- - Conditions for the target project:
- * It SHOULD delegate the `Grant`, allowing only `gatherAndConvey`, to
- its own parent projects
- * It SHOULD delegate the `Grant`, allowing only `distribute`, to teams
- to which it allows to access it
- * It SHOULD delegate the `Grant`, allowing only `invoke`, to people and
- bots to which it allows to access it
- * It SHOULD NOT make any other delegation of this `Grant`, and SHOULD
- NOT invoke it
- 2. [=actor=] is a [=Project=], [=target=] is a parent [=Project=] of it
- - Scenario: A project delegates access-to-a-resource-itself to its parent
- project
- - [=allows=] value to use: Same as 1
- - Conditions for the target project: Same as 1
- 3. [=actor=] is a component, [=target=] is a [=Team=]
- - Scenario: A component delegates access-to-a-resource-it-manages to a team
- that has been approved to access the component
- - [=allows=] value to use: [=distribute=]
- - Conditions for the target team:
- * It SHOULD delegate the `Grant`, allowing `distribute` only, to its
- subteams
- * It SHOULD delegate the `Grant`, allowing `invoke` only, to its
- members
- * It SHOULD NOT make any other delegation of this `Grant`, and SHOULD
- NOT invoke it
- 4. [=actor=] is a [=Project=], [=target=] is a [=Team=]
- - Scenario: A project delegates access-to-itself to a team that has been
- approved to access the project
- - [=allows=] value to use: Same as 3
- - Conditions for the target project: Same as 3
- #### Extending a delegation chain #### {#extending-a-delegation-chain}
- When an actor *A* receives a [=Grant=] activity *g* where the
- [=target=] is *A*, and wishes to pass on the granted access to some other actor
- *B* (who isn't the [=actor=] of that `Grant`), then actor *A* can do so by
- sending to actor *B* a new `Grant` activity *h* in which:
- - [=actor=] is actor *A*
- - [=context=] (i.e. the resource) is same as *g*'s [=context=]
- - [=target=] is actor *B*
- - [=object=] (i.e. the granted role) is either *g*'s [=object=] or a
- lower-access role than *g*'s [=object=], i.e. provides a subset of the
- permissions that *g*'s [=object=] provides (the latter case is called
- *attenuation*)
- - [=startTime=]: *(optional)* The time at which this Grant becomes valid
- - [=endTime=]: *(recommended)* The time at which this Grant expires
- - [=allows=]: Specifies the conditions under which actor *B* may
- delegate this `Grant` (i.e. conditions under which the delegation will be
- considered valid when verifying the chain), and what the recipients of
- the delegtaions that *B* will send (which are themselves `Grant` activites)
- are allowed to do with these `Grants` (invoke? further delegate to certain
- other actors?)
- - [=delegates=] is activity *g*
- - [=capability=]: *(optional)* Specifies a
- [delegate Grant](#grant-delegate) previously given by *B* to *A*
- - [=result=]: a URI that will be used later to verify that *h* is still active
- and hasn't been revoked. Alternatively, an object with [=id=] and
- [=duration=] as described below.
- The [=result=] URI MUST be provided whenever extending a delegation chain. It
- MUST be a URI that actor *A* controls, i.e. decides what will be returned by
- HTTP requests to that URI. Requirements:
- - From the moment that actor *A* publishes activity *h*, as long as actor *A*
- considers *h* an active `Grant` and hasn't revoked it, any HTTP HEAD or HTTP
- GET request the [=result=] URI MUST return an HTTP response status 204 or 200.
- - If later activity *h* is revoked, or actor *A* is deleted, then from the
- moment that actor *A* considers *h* deactivated, any HTTP HEAD or HTTP GET
- request to the [=result=] URI MUST NOT return an HTTP response status in the
- 200-299 range. The response status SHOULD be 410 or 404.
- [=result=] MAY instead specify a JSON object in which:
- - [=id=] is the URI as described above
- - *(optional)* [=duration=] specifies a duration that allows the recovation URI
- check to be skipped, if the duration hasn't yet passed since the last check
- of the URI. If [=duration=] is specified, it MUST be positive and include
- only an integral number of seconds that is less than `2^63`, and no other
- component. In other words, its format is: The string "PT", then the
- integer, then the string "S".
- In the following cases, *g* is a *request* for actor *A* to extend the
- delegation chain, and actor *A* SHOULD extend the chain by sending `Grant`
- activities, as described for each case.
- The term 'component' used below refers to a forge related service actor. This
- may be a service of a [=type=] defined in ForgeFed (such as
- [=Repository=], [=TicketTracker=],
- [=PatchTracker=]), or a service defined in some extension.
- 1. Actor *A* is a [=Project=], AND *g*'s [=actor=] is either a
- [=component=] of *A* or a [=subproject=] of
- *A*, AND *g*'s [=allows=] is a single value
- [=gatherAndConvey=]
- - Scenario: Project *A* received some access from a component/subproject of
- it, and is requested to pass it on its member people, to its member
- teams, and to its parent projects
- - Requirements for extending the delegation chain:
- 1. For each parent project *P* of project *A*, project *A* SHOULD
- publish and deliver to *P* a `Grant` activity in which:
- - [=actor=] is project *A*
- - [=context=] (i.e. the resource) is same as *g*'s [=context=]
- - [=target=] is project *P*
- - [=object=] (i.e. the granted role) is either *g*'s [=object=] or
- a lower-access role than *g*'s [=object=]
- - [=allows=] is a single value [=gatherAndConvey=]
- - [=delegates=] is activity *g*
- - [=capability=]: *(optional)* Specifies a
- [delegate Grant](#grant-delegate) previously given by *P* to *A*
- - [=result=]: a URI that will be used later to verify that *h* is
- still active and hasn't been revoked, or a JSON object as
- describes above
- 2. For each team *T* that project *A* considers a member team with role
- *p*, project *A* SHOULD publish and deliver to *T* a `Grant`
- activity in which:
- - [=actor=] is project *A*
- - [=context=] (i.e. the resource) is same as *g*'s [=context=]
- - [=target=] is team *T*
- - [=object=] (i.e. the granted role) is the lower-access role
- among *g*'s [=object=] and *p*
- - [=allows=] is a single value [=distribute=]
- - [=delegates=] is activity *g*
- - [=capability=]: *(optional)* Specifies a
- [delegate Grant](#grant-delegate) previously given by *T* to *A*
- - [=result=]: a URI that will be used later to verify that *h* is
- still active and hasn't been revoked, or a JSON object as
- describes above
- 3. For each [=Person=] or automated service bot *M* (that isn't a team)
- that project *A* considers a member with role *p*, project *A*
- SHOULD publish and deliver to *M* a `Grant` activity in which:
- - [=actor=] is project *A*
- - [=context=] (i.e. the resource) is same as *g*'s [=context=]
- - [=target=] is actor *M*
- - [=object=] (i.e. the granted role) is the lower-access role
- among *g*'s [=object=] and *p*
- - [=allows=] is a single value [=invoke=]
- - [=delegates=] is activity *g*
- - [=capability=]: *(optional)* Specifies a
- [delegate Grant](#grant-delegate) previously given by *M* to *A*
- - [=result=]: a URI that will be used later to verify that *h* is
- still active and hasn't been revoked, or a JSON object as
- describes above
- 4. Project *A* MUST NOT make any other delegations of *g*, and SHOULD
- NOT try to invoke it
- 2. Actor *A* is a [=Team=], AND *g*'s [=actor=] is either a
- component/[=Project=] in which *A* is a member or a
- parent team (see [=subteams=]) of *A*, AND *g*'s [=allows=] is a
- single value [=distribute=]
- - Scenario: Team *A* received some access from a component/project that
- considers *A* a member team, or from a parent team of *A*, and *A* is
- requested to pass it on its member people and to its subteams
- - Requirements for extending the delegation chain:
- 1. For each team *T* that team *A* considers a
- [=subteam=], team *A* SHOULD publish and deliver to *T*
- a `Grant` activity in which:
- - [=actor=] is team *A*
- - [=context=] (i.e. the resource) is same as *g*'s [=context=]
- - [=target=] is team *T*
- - [=object=] (i.e. the granted role) is the same as
- *g*'s [=object=]
- - [=allows=] is a single value [=distribute=]
- - [=delegates=] is activity *g*
- - [=capability=]: *(optional)* Specifies a
- [delegate Grant](#grant-delegate) previously given by *T* to *A*
- 2. For each [=Person=] or automated service bot *M* (that isn't a team)
- that team *A* considers a member with role *p*, team *A*
- SHOULD publish and deliver to *M* a `Grant` activity in which:
- - [=actor=] is team *A*
- - [=context=] (i.e. the resource) is same as *g*'s [=context=]
- - [=target=] is actor *M*
- - [=object=] (i.e. the granted role) is the lower-access role
- among *g*'s [=object=] and *p*
- - [=allows=] is a single value [=invoke=]
- - [=delegates=] is activity *g*
- - [=capability=]: *(optional)* Specifies a
- [delegate Grant](#grant-delegate) previously given by *M* to *A*
- 3. Team *A* MUST NOT make any other delegations of *g*, and SHOULD NOT
- try to invoke it
- #### Revoking a Grant #### {#s2s-revoke}
- At any point after an actor *A* publishes a [[#Grant]] in which it
- grants some actor *B* access to a resource that actor *A* manages, actor *A*
- MAY cancel that `Grant`, deciding it's no longer a valid OCAP to use via the
- [=capability=] property of activies that actor *B* sends.
- If actor *A* cancels such a `Grant`, it SHOULD publish and deliver, at least to
- actor *B*, a [=Revoke=] activity notifying about the canceled
- `Grant`. In the `Revoke` activity, actor *A* MUST specify the Grants being
- revoked, via the [=object=] property, where each Grant is specified in one of
- the following ways:
- 1. The Grant is specified by its `id` URI
- 2. The whole Grant activity object is provided, and MUST contain an
- [[fep-8b32|integrity proof]]
- Additional requirements:
- - Implementations displaying a `Revoke` activity or an interpretation of it in
- a human interface MUST examine the `Revoke`'s [=object=] property if it is
- present, check if any of the `Grant`s listed are delegations, and communicate
- that detail in the human interface
- Once actor *A* publishes the `Revoke`, it MUST from now on refuse to execute
- requests from actor *B* to access resources that actor *A* manages, coming as
- activities that specify any of the canceled `Grant`s in the `capability`
- property. If actor *A* receives such an activity from actor *B*, it SHOULD
- publish and send back a [=Reject=] activity, whose [=object=] specifies the
- activity that actor *B* sent.
- If the `Grant` that actor *A* is revoking specifies a [=result=], then from now
- on any HTTP HEAD request to the URI specified by [=result=] MUST NOT return an
- HTTP response status in the 200-299 range. The returned status SHOULD be 410
- or 404. See [Extending a delegation chain](#extending-a-delegation-chain) for
- more information.
- #### Verifying an invocation #### {#s2s-grant-flow}
- A [previous section](#s2s-grant-simple) described *direct* usage of
- [=Grant=]s, where the *resource actor* gives some access to a *target
- actor*, and the *target actor* then uses it to interact with the resource.
- Another way to give authorization is via delegation chains:
- - The *resource actor* passes access to a *target actor*, allowing (or
- requesting) the *target actor* to pass this access (or reduced access) on to
- more actors
- - If authorized by the delegation, those actors may further pass on the access
- (possibly reduced)
- - Eventually, an actor that received such a delegation may use it to access the
- resource
- Access is delegated using [=Grant=] activities as well, using the
- [=delegates=] property to point from each `Grant` in the chain to
- the previous one. The "direct" `Grant` discussed earlier is simply a delegation
- chain of length 1.
- When an actor *R* receives from actor *A* a request to access/modify a resource
- *r*, where the request is expressed as an activity *a* whose
- [=capability=] field specifies some other activity *g*, then *R*
- can validate *a* (i.e. decide whether or not to perform the requested action)
- using the following steps.
- *R* begins by verifying that resource *r* is indeed a resource that *R* manages
- (it may be *R* itself). Otherwise, verification has failed.
- *R* proceeds by collecting the delegation chain in a list, by traversing the
- chain backwards from the leaf all the way to the beginning of the chain. The
- traversal starts with the list *L* being empty, and *R* examines activity *g*:
- 1. *g*'s [=type=] MUST be [=Grant=]
- 2. *g*'s [=context=] MUST be *r*
- 3. *g*'s [=target=] MUST be *A*
- 4. *g* MUST NOT already be listed in *L*
- 5. Verify that *g*'s [=startTime=] <= now < *g*'s [=endTime=]
- 6. Look at *g*'s [=delegates=]:
- - If *g* doesn't specify [=delegates=]:
- 1. *g*'s [=actor=] MUST be *R*
- 2. Verify that *R* indeed published *g* and considers it an active
- grant (i.e. *R* hasn't disabled/revoked it)
- 3. Prepend *g* to the beginning of *L*, resulting with new list *M*
- 4. We're done with the traversal step, the output is *M*
- - If *g*'s [=delegates=] is some activity *h*:
- 1. *g*'s [=actor=] MUST NOT be *R*
- 2. *g* MUST specify exactly one [=result=] URI
- 3. Verify the [=result=]:
- - If it's an object with [=duration=] specified, and this duration
- of time hasn't yet passed since the last check, proceed without
- checking the URI
- - Otherwise, send an HTTP HEAD request to the URI, The HTTP
- response status MUST be 200 or 204
- 4. Prepend *g* to the beginning of *L*, resulting with new list *M*
- 5. Continue traversal by going back to step 1, but with *M* being the
- list, and with *g*'s [=actor=] instead of *A*, and now examining
- activity *h*
- Issue: "Going back to step 1" refers to the top-level list item; should
- probably tweak the CSS to display nested lists differently.
- *R* proceeds by traversing the resulting list *L* from the beginning forward,
- all the way to the leaf, validating and tracking attenuation in each step. *R*
- starts this by examining the first item in *L*, let's call this item *g*:
- 1. Let *p* be *g*'s [=object=]
- 2. Examine *g*'s position in *L*:
- - If *g* is the last item in *L*:
- 1. Perform *checkLeaf* on *g* (see below)
- 2. Verify that the action being requested by activity *a* to perform on
- resource *r* is within what *R* permits for [=Role=] *p*.
- 3. We're done with the traversal!
- - Otherwise:
- 1. Let *h* be the next item after *g* in *L*
- 2. Let *q* be *h*'s [=object=]
- 3. The permissions that role *q* allows on resource *r* MUST be
- identical to or a subset of the permissios that role *p* allows on
- *r*
- 4. Perform *checkItem* on *(g, h)* (see below)
- 5. Continue traversal by going back to step 2, but with *h* instead of
- *g* and *q* instead of *p*
- Issue: "Step 2" refers to the top-level one, need to tweak CSS for lists
- The steps *checkLeaf* and *checkItem* mentioned above MAY be extended by
- implementations, by using custom values in the [=allows=] property.
- But here are the standard definitions, using the values defined in ForgeFed:
- *checkLeaf (g):*
- 1. *g*'s [=allows=] MUST be [=invoke=]
- 2. *g*'s [=target=] (which is actor *A*, the sender of activity *a*) SHOULD be
- an actor of a [=type=] to which *R* allows to perform activity *a* on
- resource *r*, i.e. *A* should probably be a [=Person=], or some automated
- service/bot
- *checkItem (g, h):*
- 1. *g* MUST specify exactly one value for [=allows=]
- 2. That value MUST be either [=gatherAndConvey=] or [=distribute=]
- - If it's [=gatherAndConvey=]:
- 1. *g*'s [=target=] MUST be a [=Project=]
- - If it's [=distribute=]:
- 1. *g*'s [=target=] MUST be a [=Team=]
- 2. *h*'s [=allows=] MUST be either [=distribute=]
- or [=invoke=]
- At this point, activity *a* is considered authorized, and the requested action
- may be performed.
- #### Identifying resources and their managing actors #### {#manager}
- Some shared resources are themselves actors. Some shared resources aren't
- actors, but they are child objects of actors. When some actor *A* wishes to
- access a resource *R* and perform a certain operation, it needs to determine
- which actor to contact in order to request that operation. Actor *A* then looks
- at resource *R*, and the following MUST hold:
- - Either the resource *R* isn't an actor (i.e. doesn't have an [=inbox=]) but
- does specify which actor manages it via the [=managedBy=] property;
- - Or the resource *R* is an actor, i.e. it has an [=inbox=] (it doesn't have to
- specify [=managedBy=], but if it does, then it MUST refer to itself)
- Therefore any object that wishes to be specified as the [=context=] of a
- [=Grant=] MUST either be an actor or be [=managedBy=] an
- actor.
- #### Invoking a Grant
- Invoking a [=Grant=] means using the `Grant` to authorize a request to
- access or modify some resource. If some actor *A* wishes to access or modify a
- resource *r*, using a `Grant` activity *g* for authorization, preconditions
- for a successful invocation include:
- - *g*'s [=target=] is actor *A*
- - *g*'s [=context=] is either the resource *r*, or a resource in which *r* is
- contained, or the actor that [=managedBy|manages=] *r*
- - *g*'s [=object=] is a [=Role=] that permits the kind of operation
- that actor *A* is requesting to do on resource *r*
- - *g*'s [=allows=] is [=invoke=]
- - *g*'s [=startTime=] <= now < *g*'s [=endTime=]
- When actor *A* sends the activity *a* that requests to access or modify
- resource *r*, it can use *g* for authorization by specifying its [=id=] URI in
- the [=capability=] property of activity *a*.
- To have a chance to access resource *r*, actor *A* needs to deliver activity
- *a* to the actor that manages *r*. [See above](#manager) instructions for
- determining who that actor is.
- #### Time Bounds
- A [=Grant=] activity MUST be considered valid for invocation (or as a valid
- link in a delegation chain) if and only if the current time, at the time of
- invocation, is within the time bounds defined by the [=Grant=]:
- 1. A [=Grant=] MAY specify a [=startTime=]: The time at which the Grant becomes
- valid. If specified, the [=Grant=] is valid only if the time of invocation
- is equal or greater than the [=startTime=]
- 2. A [=Grant=] SHOULD specify an [=endTime=]: The time at which the Grant
- expires. If specified, the [=Grant=] is valid only if the time of
- invocation is less than the [=endTime=]
- Suggested default for picking the [=endTime=]: 6 months after publishing the
- [=Grant=].
- ### Granting access ### {#granting-access}
- #### Initial Grant upon resource creation
- When an actor *A* requests to create a new shared resource *R*, and the
- *resource actor* approves and creates it, then the *resource actor* SHOULD send
- a `Grant` to actor *A*, which provides actor *A* with access to resource *R*.
- The [=Role=] specified by the [=Grant=]'s [=object=] MUST be [=admin=], which
- means full access to *R*, including the ability to gives access-to-*R* to more
- actors (using an [=Invite=] activity, see below).
- If such a `Grant` is sent by the *resource actor* upon the creation of resource
- *R*, then the `Grant`'s [=fulfills=] property MUST be provided and
- specify the ID URI of the activity (published by actor *A*) that requested to
- create resource *R* (typically this would be a [=Create=] activity, see
- [Object Publishing and Hosting](#publishing)).
- If *R* is a [=Project=] or a [=Team=], additional steps occur:
- 1. *A*, seeing *R*'s Grant, publishes a [[#grant-delegate|delegate-Grant]]
- in which the [=target=] is *R* and [=capability=] is *R*'s Grant
- 2. From now on, whenever *R* wishes to
- [[#extending-a-delegation-chain|extend a Grant chain]] to *A*, it uses
- *A*'s delegate-Grant as the [=capability=]
- #### Offering access using Invite activities
- When an actor *A* wishes to offer actor *B* access to resource *R* (where the
- *resource actor* who manages *R* is neither *A* nor *B*), then actor *A* SHOULD
- use an [=Invite=] activity, and the following steps:
- 1. Actor *A* publishes and delivers an [=Invite=], at least to actor
- *B* and to the *resource actor* of *R*, with a relevant
- [=capability=] (see [[#Invite]] for details on the properties to use)
- 2. If actor *B* wishes to have the offered access, it publishes and delivers
- (at least to the *resource actor* of *R*) an [=Accept=] activity whose
- [=object=] specifies the `Invite` sent by actor *A*
- 3. The *resource actor* of *R* receives the `Invite` and the `Accept` and:
- 1. Verifies the `Invite` is authorized, as described above in
- [Verifying an invocation](#s2s-grant-flow)
- 2. Verifies that the `Accept`'s [=object=] specifies the `Invite` and the
- `Accept`'s [=actor=] is the `Invite`'s [=object=]
- 3. Publishes and delivers a [=Grant=] activity (see
- [[#Grant]] for more details on the properties) where:
- - [=object=] is the `Invite`'s [=instrument=]
- - [=context=] is the `Invite`'s [=target=], which is resource *R*
- - [=target=] is the `Invite`'s [=object=], which is actor *B*
- - [=fulfills=] is the `Invite`
- - [=allows=] is [=invoke=]
- - [=delegates=] isn't specified
- 4. *B* is now considered a collaborator in *R*!
- 5. If *R* is a [=Project=] or a [=Team=], additional steps occur:
- 1. *B*, seeing *R*'s Grant, publishes a [[#grant-delegate|delegate-Grant]]
- in which the [=target=] is *R* and [=capability=] is *R*'s Grant
- 2. From now on, whenever *R* wishes to
- [[#extending-a-delegation-chain|extend a Grant chain]] to *B*, it uses
- *B*'s delegate-Grant as the [=capability=]
- Actor *B* can now use the URI of that new `Grant` as the
- [=capability=] when it sends activities that access or
- manipulate resource *R*.
- #### Requesting access using Join activities
- When an actor *A* wishes to request access to resource *R* (where the *resource
- actor* who manages *R* isn't *A*), then actor *A* SHOULD use a
- [=Join=] activity, and the following steps. There are two options detailed
- below, depending on whether actor *A* has been previously given a
- [=Grant=] authorizing it to gain access to resource *R* without
- needing someone else to approve. For example, perhaps actor *A* already has
- some access to a resource collection to which *R* belongs, and that access
- allows *A* to freely `Join` *R* without needing to wait for human approval.
- **Option 1: Actor *A* already has a `Grant` allowing it to gain access to *R*
- without external approval:**
- 1. Actor *A* publishes and delivers a [=Join=], at least to the
- *resource actor* of *R*, with the relevant [=capability=] it
- has (see [[#Join]] for details on the properties
- to use)
- 2. The *resource actor* of *R* receives the `Join` and:
- 1. Verifies the `Join` is authorized, as described above in
- [Verifying an invocation](#s2s-grant-flow)
- 2. Publishes and delivers a [=Grant=] activity (see
- [[#Grant]] for more details on the
- properties) where:
- - [=object=] is the `Join`'s [=instrument=]
- - [=context=] is the `Join`'s [=object=], which is resource *R*
- - [=target=] is the `Join`'s [=actor=], which is actor *A*
- - [=fulfills=] is the `Join`
- 3. *A* is now considered a collaborator in *R*!
- 4. If *R* is a [=Project=] or a [=Team=], additional steps occur:
- 1. *A*, seeing *R*'s Grant, publishes a [[#grant-delegate|delegate-Grant]]
- in which the [=target=] is *R* and [=capability=] is *R*'s Grant
- 2. From now on, whenever *R* wishes to
- [[#extending-a-delegation-chain|extend a Grant chain]] to *A*, it uses
- *A*'s delegate-Grant as the [=capability=]
- Actor *A* can now use the URI of that new `Grant` as the
- [=capability=] when it sends activities that access or
- manipulate resource *R*.
- **Option 2: Actor *A* doesn't have (or chooses not to use) a `Grant` allowing
- it to gain access to *R* without external approval:**
- 1. Actor *A* publishes and delivers a [=Join=], at least to the
- *resource actor* of *R* (see [[#Join]] for
- details on the properties to use)
- 2. If some actor *B*, that has previously received a `Grant` from the *resource
- actor* of *R* authorizing it to approve joins, sees the `Join` sent by actor
- *A* and decides to approve it, then actor *B* publishes and delivers (at
- least to the *resource actor* of *R*) an [=Accept=] activity where:
- - [=object=] specifies the `Join` sent by actor *A*
- - [=capability=] is the `Grant` mentioned above,
- authorizing to approve or deny Joins
- 3. The *resource actor* of *R* receives the `Join` and the `Accept` and:
- 1. Verifies the `Accept` is authorized, as described above in
- [Verifying an invocation](#s2s-grant-flow)
- 2. Verifies that the `Accept`'s [=object=] specifies the `Join`
- 3. Publishes and delivers a [=Grant=] activity (see
- [[#Grant]] for more details on the properties) where:
- - [=object=] is the `Join`'s [=instrument=]
- - [=context=] is the `Join`'s [=object=], which is resource *R*
- - [=target=] is the `Join`'s [=actor=], which is actor *A*
- - [=fulfills=] is the `Join`
- 4. *A* is now considered a collaborator in *R*!
- 5. If *R* is a [=Project=] or a [=Team=], additional steps occur:
- 1. *A*, seeing *R*'s Grant, publishes a [[#grant-delegate|delegate-Grant]]
- in which the [=target=] is *R* and [=capability=] is *R*'s Grant
- 2. From now on, whenever *R* wishes to
- [[#extending-a-delegation-chain|extend a Grant chain]] to *A*, it uses
- *A*'s delegate-Grant as the [=capability=]
- Actor *A* can now use the URI of that new `Grant` as the
- [=capability=] when it sends activities that access or
- manipulate resource *R*.
- In step 2, actor *B* may choose to deny the request of actor *A*, by sending a
- [=Reject=] activity (at least to the *resource actor* of *R*) where:
- - [=object=] specifies the `Join` that actor *A* sent
- - [=capability=] is the `Grant` mentioned in step 2, authorizing
- actor *B* to approve or deny Joins
- If the *resource actor* of *R* receives the `Reject`:
- 1. It MUST verify the `Reject` is authorized, as described above in
- [Verifying an invocation](#s2s-grant-flow)
- 2. it MUST verify that the `Reject`'s [=object=] specifies the `Join`
- 2. Consider this `Join` request canceled: If actor *B*, or some other actor
- *C*, tries again to `Accept` the `Join`, then:
- 1. The *resource actor* MUST NOT send a `Grant` to actor *A*, even if the
- `Accept` is authorized
- 2. The *resource actor* MAY publish and deliver a `Reject` activity, at
- least to the actor that sent the `Accept`, where [=object=] specifies
- the `Accept`
- 4. It SHOULD publish and deliver a `Reject` activity, at least to actor *A*,
- where [=object=] specifies the `Join` that actor *A* sent
- So, once a `Join` is rejected (using an authorized `Reject`), it cannot be
- accepted. But actor *A* MAY send a new `Join`, which could then possibly get
- accepted.
- ### Revoking access
- #### Taking away access using Remove activities
- When an actor *A* wishes to cancel the membership of another actor *B* (who
- isn't *A*) in a shared resource *R*, invalidating any active
- [[#Grant]]s that the *resource actor* of *R* has granted to actor
- *B*, then actor *A* SHOULD use a [=Remove=] activity, and the following steps:
- 1. Actor *A* publishes and delivers a [=Remove=], at least to actor
- *B* and to the *resource actor* of *R*, with a relevant
- [=capability=] (see [[#Remove]]
- for details on the properties to use)
- 2. The *resource actor* of *R* receives the `Remove` and:
- 1. Verifies the `Remove` is authorized, as described above in
- [Verifying an invocation](#s2s-grant-flow)
- 2. Verifies that actor *B* indeed has active `Grant`s for accessing
- resource *R*
- 3. Marks those Grants as disabled in its internal state
- 4. Publishes and delivers a [[#Revoke]] activity, as described
- above in [Revoking a Grant](#s2s-revoke), where
- [=fulfills=] specifies the `Remove`
- Actor *B* SHOULD no longer use the URI of any `Grant` that has been disabled as
- the [=capability=] when it sends activities that access or
- manipulate resource *R*.
- #### Waiving access using Leave activities
- When an actor *A* wishes to cancel their membership in a shared resource *R*
- (where the *resource actor* who manages *R* isn't *A*), invalidating any active
- [[#Grant]]s that the *resource actor* of *R* has granted to actor
- *A*, then actor *A* SHOULD use a [=Leave=] activity, and the following steps:
- 1. Actor *A* publishes and delivers a [=Leave=], at least to the
- *resource actor* of *R* (see [[#Leave]] for
- details on the properties to use)
- 2. The *resource actor* of *R* receives the `Leave` and:
- 1. Verifies that actor *A* indeed has active `Grant`s for accessing
- resource *R*
- 2. Marks those Grants as disabled in its internal state
- 3. Publishes and delivers a [[#Revoke]] activity, as described
- above in [Revoking a Grant](#s2s-revoke), where
- [=fulfills=] specifies the `Leave`
- Actor *A* SHOULD no longer use the URI of any `Grant` that has been disabled as
- the [=capability=] when it sends activities that access or
- manipulate resource *R*.
- #### Requesting to disable specific Grants using Undo
- When an actor *A* wishes to deactivate a specific [[#Grant]] activity
- (or multiple `Grant`s), providing access to view or manipulate some resource
- *R* (where the *resource actor* of *R* isn't *A*), then actor *A* SHOULD use an
- [=Undo=] activity, and the following steps. The actor *B* to whom
- access-to-resource-*R* was given by the `Grant` may be actor *A* itself, or
- some other actor, as long as actor *A* is authorized by the *resource actor* of
- *R* to deactivate that `Grant`.
- NOTE: Upon a successful `Undo`, if actor *B* doesn't have any active `Grants`
- left, that allow access to resource *R*, then the *resource actor* of *R* MAY
- remove actor *B*'s membership in *R*, or it MAY consider actor *B* a member
- without access.
- 1. Actor *A* publishes and delivers an [=Undo=], at least to the
- *resource actor* of *R* (see [[#undo-grant]] for
- details on the properties to use)
- 2. The *resource actor* of *R* receives the `Undo` and:
- 1. Verifies the `Undo` is authorized, as described above in
- [Verifying an invocation](#s2s-grant-flow)
- 2. Verifies that actor *B* indeed has all the active `Grant`s for accessing
- resource *R*, that are listed as [=object=]s of the `Undo` (if more than
- one `Grant` is listed, the [=target=] of all the `Grant`s MUST be
- identical)
- 3. Marks all of those Grants as disabled in its internal state
- 4. Publishes and delivers a [[#Revoke]] activity, at least to
- actors *A* and *B*, as described above in
- [Revoking a Grant](#s2s-revoke), where:
- - [=object=] MUST specify all the deactivated `Grant`s
- - [=fulfills=] MUST specify the `Undo`
- Actor *B* SHOULD no longer use the URI of any `Grant` that has been disabled as
- the [=capability=] when it sends activities that access or
- manipulate resource *R*.
- ### Example
- Aviva creates a new [=Repository=] for her 3D Tree Growth
- Simulation software:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://forge.community/users/aviva/outbox/oU6QGAqr-create-treesim",
- "type": "Create",
- "actor": "https://forge.community/users/aviva",
- "to": [
- "https://forge.community/users/aviva/followers"
- ],
- "object": {
- "id": "https://forge.community/repos/treesim",
- "type": "Repository",
- "name": "Tree Growth 3D Simulation",
- "summary": "A graphical simulation of trees growing"
- }
- }
- </xmp>
- </div>
- The newly created *treesim* `Repository` automatically sends back a `Grant` to
- Aviva, allowing her full access to the repo:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://forge.community/repos/treesim/outbox/2NwyPWMX-grant-admin-to-aviva",
- "type": "Grant",
- "actor": "https://forge.community/repos/treesim",
- "to": [
- "https://forge.community/aviva",
- "https://forge.community/aviva/followers"
- ],
- "object": "admin",
- "context": "https://forge.community/repos/treesim",
- "target": "https://forge.community/aviva",
- "fulfills": "https://forge.community/users/aviva/outbox/oU6QGAqr-create-treesim",
- "allows": "invoke",
- "endTime": "2023-12-31T23:00:00-08:00"
- }
- </xmp>
- </div>
- Aviva can now use this `Grant`, e.g. to update the repo's description text:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://forge.community/users/aviva/outbox/RmTygyuj",
- "type": "Update",
- "actor": "https://forge.community/users/aviva",
- "to": [
- "https://forge.community/users/aviva/followers",
- "https://forge.community/repos/treesim",
- "https://forge.community/repos/treesim/followers"
- ],
- "object": {
- "id": "https://forge.community/repos/treesim",
- "type": "Repository",
- "name": "Tree Growth 3D Simulation",
- "summary": "Tree growth 3D simulator for my nature exploration game"
- },
- "capability": "https://forge.community/repos/treesim/outbox/2NwyPWMX-grant-admin-to-aviva"
- }
- </xmp>
- </div>
- Aviva wants to keep track of events related to the *treesim* repo:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": "https://www.w3.org/ns/activitystreams",
- "id": "https://forge.community/users/aviva/outbox/gqtpAhm2",
- "type": "Follow",
- "actor": "https://forge.community/users/aviva",
- "to": "https://forge.community/repos/treesim",
- "object": "https://forge.community/repos/treesim",
- }
- </xmp>
- </div>
- Aviva can invite Luke to have access to the *treesim* repo:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://forge.community/users/aviva/outbox/qfrEGqnC-invite-luke",
- "type": "Invite",
- "actor": "https://forge.community/users/aviva",
- "to": [
- "https://forge.community/aviva/followers",
- "https://forge.community/repos/treesim",
- "https://forge.community/repos/treesim/followers",
- "https://software.site/people/luke",
- "https://software.site/people/luke/followers"
- ],
- "instrument": "maintain",
- "target": "https://forge.community/repos/treesim",
- "object": "https://software.site/people/luke",
- "capability": "https://forge.community/repos/treesim/outbox/2NwyPWMX-grant-admin-to-aviva"
- }
- </xmp>
- </div>
- And it appears that Luke accepts the invitation:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://software.site/people/luke/activities/mEYYmt8u",
- "type": "Accept",
- "actor": "https://software.site/people/luke",
- "to": [
- "https://forge.community/aviva",
- "https://forge.community/aviva/followers",
- "https://forge.community/repos/treesim",
- "https://forge.community/repos/treesim/followers",
- "https://software.site/people/luke/followers"
- ],
- "object": "https://forge.community/users/aviva/outbox/qfrEGqnC-invite-luke"
- }
- </xmp>
- </div>
- Seeing the `Invite` and the `Accept`, the *treesim* repo sends Luke a `Grant`
- giving him the access that Aviva offered, and which he accepted:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://forge.community/repos/treesim/outbox/D5uod3pz-grant-maintainer-to-luke",
- "type": "Grant",
- "actor": "https://forge.community/repos/treesim",
- "to": [
- "https://forge.community/aviva",
- "https://forge.community/aviva/followers",
- "https://forge.community/repos/treesim/followers",
- "https://software.site/people/luke",
- "https://software.site/people/luke/followers"
- ],
- "object": "maintain",
- "context": "https://forge.community/repos/treesim",
- "target": "https://software.site/people/luke",
- "fulfills": "https://forge.community/users/aviva/outbox/qfrEGqnC-invite-luke",
- "allows": "invoke",
- "endTime": "2023-12-31T23:00:00-08:00"
- }
- </xmp>
- </div>
- Luke can now use this `Grant`, e.g. to delete some old obsolete branch of the
- *treesim* repo:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://software.site/people/luke/activities/vShj2aIe",
- "type": "Delete",
- "actor": "https://software.site/people/luke",
- "to": [
- "https://forge.community/repos/treesim",
- "https://forge.community/repos/treesim/followers",
- "https://software.site/people/luke/followers"
- ],
- "object": "https://forge.community/repos/treesim/branches/fixes-for-release-0.1.3",
- "origin": "https://forge.community/repos/treesim",
- "capability": "https://forge.community/repos/treesim/outbox/D5uod3pz-grant-maintainer-to-luke"
- }
- </xmp>
- </div>
- Celine requests to have developer access to the *treesim* repo:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.online/@celine/sent/v5Qvd6bB-celine-join",
- "type": "Join",
- "actor": "https://dev.online/@celine",
- "to": [
- "https://forge.community/repos/treesim",
- "https://forge.community/repos/treesim/followers",
- "https://dev.online/@celine/followers"
- ],
- "object": "https://forge.community/repos/treesim",
- "instrument": "write"
- }
- </xmp>
- </div>
- Aviva sees the `Join` request, talks with Celine and decides to approve her
- request:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://forge.community/users/aviva/outbox/PzRtDydu",
- "type": "Accept",
- "actor": "https://forge.community/users/aviva",
- "to": [
- "https://forge.community/repos/treesim",
- "https://forge.community/repos/treesim/followers",
- "https://dev.online/@celine",
- "https://dev.online/@celine/followers"
- ],
- "object": "https://dev.online/@celine/sent/v5Qvd6bB-celine-join",
- "capability": "https://forge.community/repos/treesim/outbox/2NwyPWMX-grant-admin-to-aviva"
- }
- </xmp>
- </div>
- Seeing the `Join` and the `Accept`, the *treesim* repo sends Celine a `Grant`
- giving her the access that she requested, and which Aviva approved:
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://forge.community/repos/treesim/outbox/D5uod3pz-grant-developer-to-celine",
- "type": "Grant",
- "actor": "https://forge.community/repos/treesim",
- "to": [
- "https://forge.community/aviva",
- "https://forge.community/repos/treesim/followers",
- "https://dev.online/@celine",
- "https://dev.online/@celine/followers"
- ],
- "object": "write",
- "context": "https://forge.community/repos/treesim",
- "target": "https://dev.online/@celine",
- "fulfills": "https://dev.online/@celine/sent/v5Qvd6bB-celine-join",
- "allows": "invoke",
- "endTime": "2023-12-31T23:00:00-08:00"
- }
- </xmp>
- </div>
- Celine can now use this `Grant` to access the *treesim* repo.
- ## Adding and Removing Team Members
- Minimal required role: [=admin=]
- This is done using the processes described in the [[#granting-access]] section.
- In particular, once the new member *M* sends the team *T* the
- [[#grant-delegate|delegate-Grant]], *T* SHOULD use it to send *M*
- [[#extending-a-delegation-chain|extension-Grants]] of:
- - Every authorized (by a valid delegate-Grant) Grant whose [=allows=] is
- [=distribute=], that *T* has received from any of its parent teams
- - Every authorized (by a valid delegate-Grant) Grant whose [=allows=] is
- [=distribute=], that *T* has received from any of the projects it has
- direct access to
- - Every authorized (by a valid delegate-Grant) Grant whose [=allows=] is
- [=distribute=], that *T* has received from any of the components it has
- direct access to
- ## Adding and Removing Project Members
- Minimal required role: [=admin=]
- This is done using the processes described in the [[#granting-access]] section.
- In particular, once the new member *M* sends the project *J* the
- [[#grant-delegate|delegate-Grant]], *J* SHOULD use it to send *M*
- [[#extending-a-delegation-chain|extension-Grants]] of:
- - Every authorized (by a valid delegate-Grant) Grant whose [=allows=] is
- [=gatherAndConvey=], that *J* has received from any of its child projects
- - Every authorized (by a valid delegate-Grant) Grant whose [=allows=] is
- [=gatherAndConvey=], that *J* has received from any of its components
- ## Associating Projects and Components
- Minimal required role: [=admin=]
- Adding and removing a component to/from a project each have 2 versions: The
- "component side" version allows an actor to initiate the action using admin
- access to the component, while the "project side" version allows to initiate
- the action using admin access to the project.
- Whenever authorization is mentioned below, it SHOULD be done using the
- [[#s2s-grant-flow|invocation verification process]].
- ### Adding a component to a project - component side
- Assuming:
- - A project *P*
- - A component *C*
- - A person with admin access to *C*, Alice
- - A person with admin access to *P*, Bob
- Alice wants to add component *C* to project *P*. The exchange of activities
- SHOULD be as follows:
- 1. Alice sends an [=Add=] activity in which:
- - [=object=] is *C*
- - [=target=] is the URI of *P*'s [=components=] collection
- - [=instrument=] is the maximal [=Role=] that Alice would like to allow for
- people (and bots) when authorizing their manipulation of *C* by their
- access in *P* (normally it would be [=admin=], perhaps sometimes
- [=maintain=])
- - [=capability=] is the URI of a [=Grant=] that authorizes admin access to
- *C*
- 2. *C*, seeing and authorizing the Add, publishes an [=Accept=] where
- [=object=] is the Add's URI
- 3. Bob, seeing the Add and the Accept, sends an [=Accept=] where:
- - [=object=] is the Add's URI
- - [=capability=] is the URI of a [=Grant=] that authorizes admin access to
- *P*
- 4. *P*, seeing the previous 3 activities and authorizing Bob's Accept,
- publishes a [[#grant-delegate|delegate-Grant]] in which the [=target=] is
- *C*
- 5. *C*, seeing *P*'s Grant, sends a [[#start-grant-chain|start-Grant]] where:
- - [=actor=] and [=context=] specify *C*
- - [=target=] specifies *P*
- - [=object=] specifies the role specified in the Add's [=instrument=]
- - [=allows=] is [=gatherAndConvey=]
- - [=capability=] is the URI of the delegate-Grant
- 6. *P*, seeing and authorizing *C*'s Grant, now
- [[#extending-a-delegation-chain|extends the Grant chain]] as relevant, to
- its members and parent projects
- ### Removing a component from a project - component side
- Assuming:
- - A project *P* with a component *C*
- - A person with admin access to *C*, Alice
- Alice wants to ask component *C* to remove itself from project *P*. The
- exchange of activities SHOULD be as follows:
- 1. Alice sends a [=Remove=] activity where:
- - [=object=] is *C*
- - [=origin=] is the URI of *P*'s [=components=] collection
- - [=tag=] is *C*
- - [=capability=] is the URI of a [=Grant=] that authorizes admin access to
- *C*
- 2. *C*, seeing and authorizing the Remove, publishes a [=Revoke=] where
- [=object=] specifies the active [[#start-grant-chain|start-Grant]] it had
- sent to *P* (and *C* now considers that Grant revoked and no longer
- considers itself as a component of *P*)
- 3. *P*, seeing the Remove and the Revoke, publishes a [=Revoke=] where
- [=object=] specifies the active [[#grant-delegate|delegate-Grant]] it had
- sent to *C* (and *P* now considers that Grant revoked and no longer
- considers *C* a component of it)
- ### Adding a component to a project - project side
- Assuming:
- - A project *P*
- - A component *C*
- - A person with admin access to *C*, Alice
- - A person with admin access to *P*, Bob
- Bob wants to add component *C* to project *P*. The exchange of activities
- SHOULD be as follows:
- 1. Bob sends an [=Invite=] activity in which:
- - [=object=] is *C*
- - [=target=] is the URI of *P*'s [=components=] collection
- - [=instrument=] is the maximal [=Role=] that Bob would like to allow for
- people (and bots) when authorizing their manipulation of *C* by their
- access in *P* (normally it would be [=admin=], perhaps sometimes
- [=maintain=])
- - [=capability=] is the URI of a [=Grant=] that authorizes admin access to
- *P*
- 2. *P*, seeing and authorizing the Invite, publishes an [=Accept=] where
- [=object=] is the Invite's URI
- 3. Alice, seeing the Invite and the Accept, sends an [=Accept=] where:
- - [=object=] is the Invite's URI
- - [=capability=] is the URI of a [=Grant=] that authorizes admin access to
- *C*
- 4. *C*, seeing the previous 3 activities and authorizing Alice's Accept, sends
- an [=Accept=] where [=object=] is the Invite's URI
- 5. *P*, seeing *C*'s Accept, publishes a [[#grant-delegate|delegate-Grant]] in
- which the [=target=] is *C*
- 6. *C*, seeing *P*'s Grant, sends a [[#start-grant-chain|start-Grant]] where:
- - [=actor=] and [=context=] specify *C*
- - [=target=] specifies *P*
- - [=object=] specifies the role specified in the Invite's [=instrument=]
- - [=allows=] is [=gatherAndConvey=]
- - [=capability=] is the URI of the delegate-Grant
- 7. *P*, seeing and authorizing *C*'s Grant, now
- [[#extending-a-delegation-chain|extends the Grant chain]] as relevant, to
- its members and parent projects
- ### Removing a component from a project - project side
- Assuming:
- - A project *P* with a component *C*
- - A person with admin access to *P*, Bob
- Bob wants to ask project *P* to remove component *C* from it. The exchange of
- activities SHOULD be as follows:
- 1. Bob sends a [=Remove=] activity where:
- - [=object=] is *C*
- - [=origin=] is the URI of *P*'s [=components=] collection
- - [=tag=] is *P*
- - [=capability=] is the URI of a [=Grant=] that authorizes admin access to
- *P*
- 3. *P*, seeing and authorizing the Remove, publishes a [=Revoke=] where
- [=object=] specifies the active [[#grant-delegate|delegate-Grant]] it had
- sent to *C* (and *P* now considers that Grant revoked and no longer
- considers *C* a component of it)
- 2. *C*, seeing the Remove and the Revoke, publishes a [=Revoke=] where
- [=object=] specifies the active [[#start-grant-chain|start-Grant]] it had
- sent to *P* (and *C* now considers that Grant revoked and no longer
- considers itself as a component of *P*)
- ## Adding and Removing Children and Parents to Projects and Teams
- Minimal required role: [=admin=]
- Like most cooperation between ForgeFed actors, the parent-child link is based
- on mutual consent:
- - To form the link, both projects/teams must explicitly agree to it
- - To remove the link, it's enough for one of them to withdraw its consent
- Parent-child links are a part of the Grant delegation system, in which the flow
- is:
- - Components initiate delegation, to:
- - The projects they belong to
- - Teams which have access to them
- - Projects delegate to their:
- - Direct collaborators
- - Parent projects
- - Teams which have access to them
- - Teams delegate to their:
- - Direct collaborators
- - Child teams
- Once a parent-child link is formed, delegations start happening through the
- link. This is described in more detail below.
- Each delegation within this flow therefore has a "source" side, which sends the
- delegation, and a "destination" side, which receives it and possibly delegates
- it further.
- In a parent-child link, therefore, there is a "source" side and a "destination"
- side. For projects, the child is the source and the parent is the destination
- (because projects delegate to their parents, once a link is formed). For teams,
- the parent is the source and the child is the destination (because teams
- delegate to their children, once a link is formed).
- Since either side of the link can initiate the process, and since delegation
- flow occurs in one direction, there are essentially two versions of the
- process:
- - Process initiated by the source side (i.e. a project asking to have a parent,
- or a team asking to have a child)
- - Process initiated by the destination side (i.e. a project asking to have a
- child, or a team asking to have a parent)
- In the [=Add=] activity that initiates the process, the initiating side is
- determined by the [=target=] property. The actor to whom the collection
- specified by [=target=] belongs, is the initiating side. The other side of the
- link is specified in the [=object=] property.
- Whenever authorization is mentioned below, it SHOULD be done using the
- [[#s2s-grant-flow|invocation verification process]].
- ### Forming a project child-parent link
- #### Initiated by the parent
- Assuming:
- - A project *P*
- - A project *C*
- - A person with admin access to *C*, Celia
- - A person with admin access to *P*, Philip
- Philip wants *C* to become a child of *P* (which means *P* will be a parent
- of *C*). The exchange of activities SHOULD be as follows:
- 1. Philip sends an [=Add=] activity in which:
- - [=object=] is *C*
- - [=target=] is the URI of *P*'s [=subprojects=] (i.e. children) collection
- - [=instrument=] is the maximal [=Role=] that Philip would like to allow
- for people (and bots) when authorizing their manipulation of *C* by
- their access in *P* (normally it would be [=admin=], perhaps sometimes
- [=maintain=])
- - [=capability=] is the URI of a [=Grant=] that authorizes Philip's admin
- access to *P*
- 2. *P*, seeing and authorizing the Add, publishes an [=Accept=] where
- [=object=] is the Add's URI
- 3. Celia, seeing the Add and the Accept, sends an [=Accept=] where:
- - [=object=] is the Add's URI
- - [=capability=] is the URI of a [=Grant=] that authorizes Celia's admin
- access to *C*
- 4. *C*, seeing the Add and authorizing Celia's Accept, publishes an [=Accept=]
- where [=object=] is the Add's URI
- 5. *P*, seeing *C*'s Accept, publishes a [[#grant-delegate|delegate-Grant]] in
- which the [=target=] is *C*, and now considers *C* a child and lists *C* in
- the [=subprojects=] collection
- 6. *C*, seeing *P*'s Grant, now considers *P* a parent and lists *P* in the
- [=context=] (i.e. parents) collection
- Now *C* sends delegation Grant activities, see the [[#deleg-step-j]] section
- below.
- #### Initiated by the child
- Assuming:
- - A project *P*
- - A project *C*
- - A person with admin access to *C*, Celia
- - A person with admin access to *P*, Philip
- Celia wants *C* to become a child of *P* (which means *P* will be a parent
- of *C*). The exchange of activities SHOULD be as follows:
- 1. Celia sends an [=Add=] activity in which:
- - [=object=] is *P*
- - [=target=] is the URI of *C*'s [=context=] (i.e. parents) collection
- - [=instrument=] is the maximal [=Role=] that Celia would like to allow
- for people (and bots) when authorizing their manipulation of *C* by
- their access in *P* (normally it would be [=admin=], perhaps sometimes
- [=maintain=])
- - [=capability=] is the URI of a [=Grant=] that authorizes Celia's admin
- access to *C*
- 2. *C*, seeing and authorizing the Add, publishes an [=Accept=] where
- [=object=] is the Add's URI
- 3. Philip, seeing the Add and the Accept, sends an [=Accept=] where:
- - [=object=] is the Add's URI
- - [=capability=] is the URI of a [=Grant=] that authorizes Philip's admin
- access to *P*
- 4. *P*, seeing the Add, seeing *C*'s Accept, and authorizing Philip's Accept,
- publishes a [[#grant-delegate|delegate-Grant]] in which the [=target=] is
- *C*, and now considers *C* a child and lists *C* in the [=subprojects=]
- collection
- 5. *C*, seeing *P*'s Grant, now considers *P* a parent and lists *P* in the
- [=context=] (i.e. parents) collection
- Now *C* sends delegation Grant activities, see the [[#deleg-step-j]] section
- below.
- #### Delegation step #### {#deleg-step-j}
- *C* now sends Grant activities, which *P* delegates further by sending its own
- Grant activities, as described in [[#extending-a-delegation-chain]], to its
- member humans, member teams, and parent projects.
- 1. *C* sends a [[#start-grant-chain|start-Grant]] where:
- - [=actor=] and [=context=] specify *C*
- - [=target=] specifies *P*
- - [=object=] specifies the role specified in the Add's [=instrument=]
- - [=allows=] is [=gatherAndConvey=]
- - [=capability=] is the URI of the delegate-Grant from step 5 or 4 above
- 2. For each delegation Grant *g* that *C* has received from its components and
- child projects, *C* sends an
- [[#extending-a-delegation-chain|extension-Grant]] where:
- - [=actor=] specifies *C*
- - [=context=] is identical to *g*'s [=context=]
- - [=target=] specifies *P*
- - [=object=] specifies the lower role among the one specified by the
- Add's [=instrument=], and the one specified by *g*'s [=object=]
- - [=allows=] is [=gatherAndConvey=]
- - [=capability=] is the URI of the delegate-Grant from step 5 or 4
- above
- - [=delegates=] specifies *g*'s URI
- When *P* sees these Grants, it delegates them further, as mentioned above.
- In addition, as long as the parent-child link remains active, whenever *C*
- receives a delegation Grant from any of its components or children, it SHOULD
- send *P* an extension-Grant as described above, and *P* SHOULD extend it
- further.
- ### Removing a project child-parent link
- #### Initiated by the child
- Assuming:
- - Projects *P* and *C*, where *P* is a parent of *C*
- - A person with admin access to *C*, Celia
- Celia wants *C* to stop being a child of *P* (which means *P* will stop
- being a parent of *C*). The exchange of activities SHOULD be as follows:
- 1. Celia sends an [=Remove=] activity in which:
- - [=object=] is *P*
- - [=origin=] is the URI of *C*'s [=context=] (i.e. parents) collection
- - [=capability=] is the URI of a [=Grant=] that authorizes Celia's admin
- access to *C*
- 2. *C*, seeing and authorizing the Remove, publishes an [=Accept=] where
- [=object=] is the Add's URI
- 3. *P*, seeing the Remove and the Accept, publishes a [=Revoke=] activity in
- which the [=object=] is the delegate-Grant it had sent to *C*, and now
- considers that Grant revoked, and removes *C* from its [=subprojects=]
- collection
- 4. *C*, seeing *P*'s Revoke, removes *P* from its [=context=] (i.e. parents)
- collection
- Now, for each Grant *g* that *P* has published, as a extension of a Grant it
- had received from *C*:
- 1. *P* sends (to *g*'s [=target=]) a [=Revoke=] activity whose [=object=]
- specifies *g*'s URI
- 2. *P* considers *g* invalidated
- 3. The recipient of the [=Revoke=], if it further published extensions of *g*,
- now similarly publishes Revoke activities and considers those extension
- Grants invalid
- #### Initiated by the parent
- Assuming:
- - Projects *P* and *C*, where *P* is a parent of *C*
- - A person with admin access to *P*, Philip
- Philip wants *C* to stop being a child of *P* (which means *P* will stop
- being a parent of *C*). The exchange of activities SHOULD be as follows:
- 1. Philip sends an [=Remove=] activity in which:
- - [=object=] is *C*
- - [=origin=] is the URI of *P*'s [=subprojects=] (i.e. children) collection
- - [=capability=] is the URI of a [=Grant=] that authorizes Philip's admin
- access to *P*
- 2. *P*, seeing and authorizing the Remove, publishes a [=Revoke=] activity in
- which the [=object=] is the delegate-Grant it had sent to *C*, and now
- considers that Grant revoked, and removes *C* from its [=subprojects=]
- collection
- 3. *C*, seeing *P*'s Revoke, removes *P* from its [=context=] (i.e. parents)
- collection, and MAY publish an Accept (whose [=object=] is the Remove) to
- notify its followers
- Now, for each Grant *g* that *P* has published, as a extension of a Grant it
- had received from *C*:
- 1. *P* sends (to *g*'s [=target=]) a [=Revoke=] activity whose [=object=]
- specifies *g*'s URI
- 2. *P* considers *g* invalidated
- 3. The recipient of the [=Revoke=], if it further published extensions of *g*,
- now similarly publishes Revoke activities and considers those extension
- Grants invalid
- ### Forming a team child-parent link
- #### Initiated by the parent
- Assuming:
- - A team *P*
- - A team *C*
- - A person with admin access to *C*, Celia
- - A person with admin access to *P*, Philip
- Philip wants *C* to become a child of *P* (which means *P* will be a parent
- of *C*). The exchange of activities SHOULD be as follows:
- 1. Philip sends an [=Add=] activity in which:
- - [=object=] is *C*
- - [=target=] is the URI of *P*'s [=subteams=] (i.e. children) collection
- - [=instrument=] is the maximal [=Role=] that Philip would like to allow
- for people (and bots) when authorizing their manipulation of *C* by
- their access in *P* (normally it would be [=admin=], perhaps sometimes
- [=maintain=])
- - [=capability=] is the URI of a [=Grant=] that authorizes Philip's admin
- access to *P*
- 2. *P*, seeing and authorizing the Add, publishes an [=Accept=] where
- [=object=] is the Add's URI
- 3. Celia, seeing the Add and the Accept, sends an [=Accept=] where:
- - [=object=] is the Add's URI
- - [=capability=] is the URI of a [=Grant=] that authorizes Celia's admin
- access to *C*
- 4. *C*, seeing the Add, seeing *P*'s Accept, and authorizing Celia's Accept,
- publishes a [[#grant-delegate|delegate-Grant]] in which the [=target=] is
- *P*, and now considers *P* a parent and lists *P* in the [=context=] (i.e.
- parents) collection
- 5. *P*, seeing *C*'s Grant, now considers *C* a parent and lists *C* in the
- [=subteams=] collection
- Now *P* sends delegation Grant activities, see the [[#deleg-step-t]] section
- below.
- #### Initiated by the child
- Assuming:
- - A team *P*
- - A team *C*
- - A person with admin access to *C*, Celia
- - A person with admin access to *P*, Philip
- Celia wants *C* to become a child of *P* (which means *P* will be a parent
- of *C*). The exchange of activities SHOULD be as follows:
- 1. Celia sends an [=Add=] activity in which:
- - [=object=] is *P*
- - [=target=] is the URI of *C*'s [=context=] (i.e. parents) collection
- - [=instrument=] is the maximal [=Role=] that Celia would like to allow
- for people (and bots) when authorizing their manipulation of *C* by
- their access in *P* (normally it would be [=admin=], perhaps sometimes
- [=maintain=])
- - [=capability=] is the URI of a [=Grant=] that authorizes Celia's admin
- access to *C*
- 2. *C*, seeing and authorizing the Add, publishes an [=Accept=] where
- [=object=] is the Add's URI
- 3. Philip, seeing the Add and the Accept, sends an [=Accept=] where:
- - [=object=] is the Add's URI
- - [=capability=] is the URI of a [=Grant=] that authorizes Philip's admin
- access to *P*
- 4. *P*, seeing the Add, seeing *C*'s Accept, and authorizing Philip's Accept,
- publishes an [=Accept=] where [=object=] is the Add's URI
- 5. *C*, seeing *P*'s Accept, publishes a [[#grant-delegate|delegate-Grant]] in
- which the [=target=] is *P*, and now considers *P* a parent and lists *P*
- in the [=context=] (i.e. parents) collection
- 6. *P*, seeing *C*'s Grant, now considers *C* a child and lists *C* in the
- [=subteams=] collection
- Now *P* sends delegation Grant activities, see the [[#deleg-step-t]] section
- below.
- #### Delegation step #### {#deleg-step-t}
- *P* now sends Grant activities, which *C* delegates further by sending its own
- Grant activities, as described in [[#extending-a-delegation-chain]], to its
- member humans and child teams.
- 1. *P* sends a [[#start-grant-chain|start-Grant]] where:
- - [=actor=] and [=context=] specify *P*
- - [=target=] specifies *C*
- - [=object=] specifies the role specified in the Add's [=instrument=]
- - [=allows=] is [=distribute=]
- - [=capability=] is the URI of the delegate-Grant from step 4 or 5 above
- 2. For each delegation Grant *g* that *P* has received from its projects,
- components or parent teams, *P* sends an
- [[#extending-a-delegation-chain|extension-Grant]] where:
- - [=actor=] specifies *P*
- - [=context=] is identical to *g*'s [=context=]
- - [=target=] specifies *C*
- - [=object=] specifies the lower role among the one specified by the
- Add's [=instrument=], and the one specified by *g*'s [=object=]
- - [=allows=] is [=distribute=]
- - [=capability=] is the URI of the delegate-Grant from step 4 or 5 above
- - [=delegates=] specifies *g*'s URI
- When *C* sees these Grants, it delegates them further, as mentioned above.
- In addition, as long as the parent-child link remains active, whenever *P*
- receives a delegation Grant from any of its projects, components or parents, it
- SHOULD send *C* an extension-Grant as described above, and *C* SHOULD extend it
- further.
- ### Removing a team child-parent link
- #### Initiated by the child
- Assuming:
- - Teams *P* and *C*, where *P* is a parent of *C*
- - A person with admin access to *C*, Celia
- Celia wants *C* to stop being a child of *P* (which means *P* will stop
- being a parent of *C*). The exchange of activities SHOULD be as follows:
- 1. Celia sends an [=Remove=] activity in which:
- - [=object=] is *P*
- - [=origin=] is the URI of *C*'s [=context=] (i.e. parents) collection
- - [=capability=] is the URI of a [=Grant=] that authorizes Celia's admin
- access to *C*
- 2. *C*, seeing and authorizing the Remove, publishes a [=Revoke=] activity in
- which the [=object=] is the delegate-Grant it had sent to *P*, and now
- considers that Grant revoked, and removes *P* from its [=context=] (i.e.
- parents) collection
- 3. *P*, seeing *C*'s Revoke, removes *C* from its [=subteams=] collection,
- and MAY publish an Accept (whose [=object=] is the Remove) to notify its
- followers
- Now, for each Grant *g* that *C* has published, as a extension of a Grant it
- had received from *P*:
- 1. *C* sends (to *g*'s [=target=]) a [=Revoke=] activity whose [=object=]
- specifies *g*'s URI
- 2. *C* considers *g* invalidated
- 3. The recipient of the [=Revoke=], if it further published extensions of *g*,
- now similarly publishes Revoke activities and considers those extension
- Grants invalid
- #### Initiated by the parent
- Assuming:
- - Teams *P* and *C*, where *P* is a parent of *C*
- - A person with admin access to *P*, Philip
- Philip wants *C* to stop being a child of *P* (which means *P* will stop
- being a parent of *C*). The exchange of activities SHOULD be as follows:
- 1. Philip sends an [=Remove=] activity in which:
- - [=object=] is *C*
- - [=origin=] is the URI of *P*'s [=subteams=] (i.e. parents) collection
- - [=capability=] is the URI of a [=Grant=] that authorizes Philip's admin
- access to *P*
- 2. *P*, seeing and authorizing the Remove, publishes an [=Accept=] where
- [=object=] is the Add's URI
- 3. *C*, seeing the Remove and the Accept, publishes a [=Revoke=] activity in
- which the [=object=] is the delegate-Grant it had sent to *P*, and now
- considers that Grant revoked, and removes *P* from its [=context=] (i.e.
- parents) collection
- 4. *P*, seeing *C*'s Revoke, removes *C* from its [=subteams=] collection
- Now, for each Grant *g* that *C* has published, as a extension of a Grant it
- had received from *P*:
- 1. *C* sends (to *g*'s [=target=]) a [=Revoke=] activity whose [=object=]
- specifies *g*'s URI
- 2. *C* considers *g* invalidated
- 3. The recipient of the [=Revoke=], if it further published extensions of *g*,
- now similarly publishes Revoke activities and considers those extension
- Grants invalid
- # Actor Interface
- Issue: This section will provide, for each actor type, a summary of the various
- (1) "methods" i.e. activities it can receive as requests for action; (2)
- "events", i.e. activities it sends out; (3) perhaps representation of resources
- that are specific to the actor type. Right now there's a grey zone between
- methods and events, because some activities are methods for the target actor
- but events for any other recipient, and some events are actually sent by other
- actors, which cc the target actor's followers and the target actor delivers via
- inbox-forwarding and not by `Announce`ing (or custom `Forward`ing) those
- activities.
- ## Person ## {#person-iface}
- ## Team ## {#team-iface}
- ## Project ## {#project-iface}
- ## Repository ## {#repo-iface}
- ## TicketTracker ## {#tt-iface}
- ## PatchTracker ## {#pt-iface}
- # Client to Server Interactions
- Issue: This section is about how a human or bot can interact with the system by
- POSTing activities into the outbox of a Person (or Application/Service?) actor,
- and managing notifications. It's less urgent than Server-to-Server. fr33 is
- using C2S in Vervis, and will be gradually working on this part.
- ForgeFed uses Activities for client to server interactions, as described by
- ActivityPub. A client will send objects (eg. a Ticket) wrapped in a Activity
- (eg. Create) to an actor's outbox, and in turn the server will take care of
- delivery.
- ## Follow Activity
- The Follow activity is used to subscribe to the activities of a Repository.
- The client MUST send a Follow activity to the Person's outbox. The server
- in turn delivers the message to the destination inbox.
- ## Push Activity
- The Push activity is used to notify followers when somebody has pushed changes
- to a Repository.
- The client MUST send a Push activity to the Repository's outbox. The server
- in turn delivers the message to the Repository followers.
- # Actor and Resource Representation
- Issue: This section is about representation of objects. It's possible that
- actor representation will move into a separate section, as well as objects
- specific to one actor type. And then this section will describe only
- objects/resources used by multiple actor types, such as comments (person, issue
- tracker, PR tracker) and tickets (used for both issues and PRs).
- ## Comment ## {#Comment}
- To represent a comment, e.g. a comment on a ticket or a merge request, use the
- ActivityPub [=Note=] type.
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Note=]
- [=attributedTo=]:
- The author of the comment
- [=context=]:
- The topic of the discussion, e.g. a ticket or a merge request. It MUST be
- provided.
- [=inReplyTo=]:
- The entity on which this comment replies. MUST be provided. If the comment
- is made directly on the discussion topic, then [=inReplyTo=] MUST be
- identical to [=context=]. Otherwise, set [=inReplyTo=] to the comment to
- which this comment replies. In that case both comments MUST have an
- identical [=context=].
- [=content=], [=mediaType=], [=source=]:
- The comment text, in rendered form and in source form
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": "https://www.w3.org/ns/activitystreams",
- "id": "https://forge.example/luke/comments/rD05r",
- "type": "Note",
- "attributedTo": "https://forge.example/luke",
- "context": "https://dev.example/aviva/game-of-life/merge-requests/19",
- "inReplyTo": "https://dev.example/aviva/comments/E9AGE",
- "mediaType": "text/html",
- "content": "<p>Thank you for the review! I'll submit a correction ASAP</p>",
- "source": {
- "mediaType": "text/markdown; variant=Commonmark",
- "content": "Thank you for the review! I'll submit a correction ASAP"
- },
- "published": "2019-11-06T20:49:05.604488Z"
- }
- </xmp>
- </div>
- ## Team Membership
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Relationship=]
- [=subject=]:
- A [=Team=]
- [=relationship=]:
- [=hasMember=]
- [=object=]:
- A [=Person=] who is a member of the `Team`
- [=tag=]:
- The role that the member has in the team
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/teams/mobilizon-dev-team/members/ThmsicTj",
- "type": "Relationship",
- "subject": "https://dev.example/teams/mobilizon-dev-team",
- "relationship": "hasMember",
- "object": "https://dev.example/people/celine",
- "tag": "develop"
- }
- </xmp>
- </div>
- ## Team
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Team=]
- [=name=]:
- The user-given name of the team, e.g. "Gitea Development Team"
- [=published=]:
- The time the team was created on the server
- [=summary=]:
- A one-line user provided description of the project, as HTML, e.g.
- `"We are creating a code hosting platform"`
- [=members=]:
- [=Collection=] of the members of this team
- [=subteams=]:
- Subteams of this team, i.e. teams whose members (and subteams) inherit the
- access that this team has been granted (to projects, repositories, etc.)
- [=context=]:
- Parent [=Team=]s of this team, i.e. teams from which this team inherits
- access to projects, components and resources, e.g. repositories, ticket
- trackers (and passes them to its [=members=] and inherits them to its own
- [=subteams=])
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v2",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/teams/mobilizon-dev-team",
- "type": "Team",
- "name": "Mobilizon Development Team",
- "summary": "We're creating a federated tool for organizing events!",
- "members": {
- "type": "Collection",
- "totalItems": 3,
- "items": [
- { "type": "Relationship",
- "subject": "https://dev.example/teams/mobilizon-dev-team",
- "relationship": "hasMember",
- "object": "https://dev.example/people/alice",
- "tag": "admin"
- },
- { "type": "Relationship",
- "subject": "https://dev.example/teams/mobilizon-dev-team",
- "relationship": "hasMember",
- "object": "https://dev.example/people/bob",
- "tag": "maintain"
- },
- { "type": "Relationship",
- "subject": "https://dev.example/teams/mobilizon-dev-team",
- "relationship": "hasMember",
- "object": "https://dev.example/people/celine",
- "tag": "develop"
- }
- ]
- },
- "subteams": {
- "type": "Collection",
- "totalItems": 2,
- "items": [
- "https://dev.example/teams/mobilizon-backend-team",
- "https://dev.example/teams/mobilizon-frontend-team"
- ]
- },
- "context": "https://dev.example/teams/framasoft-developers",
- "publicKey": {
- "id": "https://dev.example/teams/mobilizon-dev-team#main-key",
- "owner": "https://dev.example/teams/mobilizon-dev-team",
- "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
- },
- "inbox": "https://dev.example/teams/mobilizon-dev-team/inbox",
- "outbox": "https://dev.example/teams/mobilizon-dev-team/outbox",
- "followers": "https://dev.example/teams/mobilizon-dev-team/followers"
- }
- </xmp>
- </div>
- ## Project ## {#Project}
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Project=]
- [=name=]:
- The user-given name of the project, e.g. "My cool project"
- [=published=]:
- The time the project was created on the server
- [=summary=]:
- A one-line user provided description of the project, as HTML, e.g.
- "`<p>A command-line tool that does cool things</p>`"
- [=ticketsTrackedBy=]:
- The default ticket tracker to use when submitting a ticket to this project
- (this tracker MUST be listed under the project's [=components=])
- [=subprojects=]:
- A [=Collection=] of the subprojects of this project
- [=context=]:
- The parent [=Project=](s) to which this project belongs
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/projects/wanderer",
- "type": "Project",
- "name": "Wanderer",
- "summary": "3D nature exploration game",
- "components": {
- "type": "Collection",
- "totalItems": 7,
- "items": [
- "https://dev.example/repos/opengl-vegetation",
- "https://dev.example/repos/opengl-vegetation/patch-tracker",
- "https://dev.example/repos/treesim",
- "https://dev.example/repos/treesim/patch-tracker",
- "https://dev.example/repos/wanderer",
- "https://dev.example/repos/wanderer/patch-tracker",
- "https://dev.example/issue-trackers/wanderer"
- ]
- },
- "subprojects": {
- "type": "Collection",
- "totalItems": 2,
- "items": [
- "https://dev.example/projects/nature-3d-models",
- "https://dev.example/projects/wanderer-fundraising"
- ]
- },
- "ticketsTrackedBy": "https://dev.example/issue-trackers/wanderer",
- "inbox": "https://dev.example/projects/wanderer/inbox",
- "outbox": "https://dev.example/projects/wanderer/outbox",
- "followers": "https://dev.example/projects/wanderer/followers"
- }
- </xmp>
- </div>
- ## Commit
- To represent a named set of changes committed into a repository's history, use
- the ForgeFed [=Commit=] type. Such a committed change set is called
- e.g. a *commit* in Git, and a *patch* in Darcs.
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Commit=]
- [=context=]:
- The [=Repository=] that this commit belongs to
- [=attributedTo=]:
- The commit author; if their actor URI is unknown, it MAY be their email
- address as a `mailto` URI
- [=created=]:
- A value of type [=dateTime=] (i.e. an ISO 8601 datetime value) specifying
- the time at which the commit was written by its author
- [=committedBy=]:
- The entity that committed the commit's changes into their local copy of the
- repo, before the commit was pushed; if their actor URI is unknown, it MAY
- be their email address as a `mailto` URI
- [=committed=]:
- The time the commit was committed by its committer
- [=hash=]:
- The hash identifying the commit, e.g. the commit SHA1 hash in Git; the
- patch info SHA1 hash in Darcs
- [=summary=]:
- The commit's one-line title as HTML-escaped plain text; if the commit title
- and description are a single commit message string, then the title is the
- 1st line of the commit message
- [=description=]:
- A JSON object with a [=mediaType=] field and a [=content=] field, where
- `mediaType` SHOULD be "text/plain" and `content` is the commit's
- possibly-multi-line description; if the commit title and description are a
- single commit message string, then the description is everything after the
- 1st line of the commit message (possibly with leading whitespace stripped)
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/alice/myrepo/commits/109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
- "type": "Commit",
- "context": "https://example.dev/alice/myrepo",
- "attributedTo": "https://example.dev/bob",
- "created": "2019-07-11T12:34:56Z",
- "committedBy": "https://example.dev/alice",
- "committed": "2019-07-26T23:45:01Z",
- "hash": "109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
- "summary": "Add an installation script, fixes issue #89",
- "description": {
- "mediaType": "text/plain",
- "content": "It's about time people can install it on their computers!"
- }
- }
- </xmp>
- </div>
- ## Branch
- To represent a repository branch, use the ForgeFed [=Branch=] type.
- It can be a real built-in version control system branch (such as a Git branch)
- or a copy of the repo used as a branch (e.g. in Darcs, which doesn't implement
- branches, and the way to have branches is to keep multiple versions of the
- repo).
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Branch=]
- [=context=]:
- The [=Repository=] that this branch belongs to
- [=name=]:
- The user given name of the branch, e.g. "main"
- [=ref=]:
- The unique identifier of the branch within the repo, e.g. "refs/heads/main"
- [=team=]:
- If the branch has its own access/authority/visibility settings, this can be
- a [=Collection=] of the actors who have push/edit access to the branch
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/luke/myrepo/branches/master",
- "type": "Branch",
- "context": "https://example.dev/luke/myrepo",
- "name": "master",
- "ref": "refs/heads/master"
- }
- </xmp>
- </div>
- ## Repository ## {#Repository}
- To represent a version control repository, use the ForgeFed
- [=Repository=] type.
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Repository=]
- [=name=]:
- The user given name of the repository, e.g. "My cool repo"
- [=cloneUri=]:
- The endpoint from which the content of the repository can be obtained via
- the native protocol (Git, Hg, etc.)
- [=attributedTo=]:
- The actor(s) in charge of the repository, e.g. a person or an organization;
- if their actor URI is unknown, it MAY be their email address as a `mailto`
- URI
- [=published=]:
- The time the repository was created on the server
- [=summary=]:
- A one-line user provided description of the repository, as HTML, e.g.
- "`<p>A command-line tool that does cool things</p>`"
- [=team=]:
- [=Collection=] of actors who have management/push access to the repository,
- or the subset of them who is available and wants to be
- contacted/notified/responsible on repo access related activities/requests
- [=forks=]:
- [=OrderedCollection=] of repositories that are forks of this repository
- [=ticketsTrackedBy=]:
- The ticket tracker that tracks tickets for this repository, this can be the
- repository itself if it manages its own tickets
- [=sendPatchesTo=]:
- The actor that tracks patches for this repository, this can be the
- repository itself if it manages its own patches and merge requests. For
- example it may be some external tracker or service, or the user or team to
- whom the repository belongs.
- [=context=]:
- The [=Project=](s) to which this repository belongs
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v1",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/treesim",
- "cloneUri": "https://dev.example/aviva/treesim.git",
- "type": "Repository",
- "publicKey": {
- "id": "https://dev.example/aviva/treesim#main-key",
- "owner": "https://dev.example/aviva/treesim",
- "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
- },
- "inbox": "https://dev.example/aviva/treesim/inbox",
- "outbox": "https://dev.example/aviva/treesim/outbox",
- "followers": "https://dev.example/aviva/treesim/followers",
- "team": "https://dev.example/aviva/treesim/team",
- "ticketsTrackedBy": "https://dev.example/aviva/treesim",
- "sendPatchesTo": "https://dev.example/aviva/treesim",
- "name": "Tree Growth 3D Simulation",
- "attributedTo": "https://example.dev/bob",
- "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>"
- }
- </xmp>
- </div>
- ## Push ## {#Push}
- To represent an event of [=Commit=]s being pushed to a
- [=Repository=], use a ForgeFed [=Push=] activity.
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Push=]
- [=actor=]:
- The [=Repository=] to which the push was made, and that is publishing this
- Push activity
- [=attributedTo=]:
- The entity (person, bot, etc.) that pushed the commits
- [=target=]:
- The specific repo history tip onto which the commits were added, this is
- either a [=Branch=] (for VCSs that have branches) or a [=Repository=] (for
- VCSs that don't have branches, only a single history line). If it's a
- branch, it MUST be a branch belonging to the repository specified by
- [=actor=]. And if it's a repository, it MUST be identical to the one
- specified by [=actor=].
- [=hashBefore=]:
- Repo/branch/tip hash before adding the new commits
- [=hashAfter=]:
- Repo/branch/tip hash after adding the new commits
- [=object=]:
- An [=OrderedCollection=] of the [=Commit=]s being pushed, in **reverse
- chronological order**. The [=items=] (or [=orderedItems=]) property of the
- collection MUST contain either the whole list of commits being pushed, or a
- prefix i.e. continuous subset from the beginning of the list (therefore the
- **latest** commits). [=earlyItems=] MAY be used for listing a suffix i.e.
- continuous subset from the end (therefore the **earliest** commits).
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/outbox/E26bE",
- "type": "Push",
- "actor": "https://dev.example/aviva",
- "to": [
- "https://dev.example/aviva/followers",
- "https://dev.example/aviva/game-of-life",
- "https://dev.example/aviva/game-of-life/team",
- "https://dev.example/aviva/game-of-life/followers"
- ],
- "context": "https://dev.example/aviva/game-of-life",
- "target": "https://dev.example/aviva/game-of-life/branches/master",
- "hashBefore": "017cbb00bc20d1cae85f46d638684898d095f0ae",
- "hashAfter": "be9f48a341c4bb5cd79ae7ab85fbf0c05d2837bb",
- "object": {
- "totalItems": 2,
- "type": "OrderedCollection",
- "orderedItems": [
- {
- "id": "https://dev.example/aviva/game-of-life/commits/be9f48a341c4bb5cd79ae7ab85fbf0c05d2837bb",
- "type": "Commit",
- "attributedTo": "https://dev.example/aviva",
- "context": "https://dev.example/aviva/game-of-life",
- "hash": "be9f48a341c4bb5cd79ae7ab85fbf0c05d2837bb",
- "created": "2019-12-02T16:07:32Z",
- "summary": "Add widget to alter simulation speed"
- },
- {
- "id": "https://dev.example/aviva/game-of-life/commits/fa37fe100a8b1e69933889c5bf3caf95cd3ae1e6",
- "type": "Commit",
- "attributedTo": "https://dev.example/aviva",
- "context": "https://dev.example/aviva/game-of-life",
- "hash": "fa37fe100a8b1e69933889c5bf3caf95cd3ae1e6",
- "created": "2019-12-02T15:51:52Z",
- "summary": "Set window title correctly, fixes issue #7"
- }
- ]
- }
- }
- </xmp>
- </div>
- ## Ticket ## {#Ticket}
- To represent a work item in a project, use the ForgeFed [=Ticket=]
- type.
- TODO decide on ticket categories/subtypes and update below
- TODO decide on property for titles, update below
- TODO properly document `history` or remove it from example
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Ticket=]
- [=context=]:
- The [=TicketTracker=] or [=PatchTracker=] to which this ticket belongs
- [=attributedTo=]:
- The actor (person, bot, etc.) who submitted the ticket
- [=summary=]:
- The ticket's one-line title, as HTML-escaped plain text
- [=content=], [=mediaType=]:
- The ticket's (possibly multi-line) detailed description text, in rendered
- form
- [=source=]:
- Source form of the ticket's description
- [=published=]:
- The time the ticket submission was accepted (which may not be the same as
- the time the ticket was submitted)
- [=followers=]:
- Collection of the followers of the ticket, actors who want to be notified
- on activity related to the ticket
- [=team=]:
- Collection of project team members who have responsibility for work on this
- ticket and want to be notified on activities related to it
- [=replies=]:
- Collection of direct comments made on the ticket (but not comments made *on
- other* comments on the ticket)
- [=dependants=]:
- Collection of [=Ticket=]s which depend on this ticket
- [=dependencies=]:
- Collection of [=Ticket=]s on which this ticket depends
- [=isResolved=]:
- Whether the work on this ticket is done
- [=resolvedBy=]:
- If the work on this ticket is done, who marked the ticket as resolved, or
- which activity did so
- [=resolved=]:
- When the ticket has been marked as resolved
- </pre>
- There's an important distinction between these two kinds of tickets:
- - Task:
- A work item which tracks some task to be done, in which the task and its
- results are described in text, but the work itself is done elsewhere. This
- is often called "issue" in software project hosting platforms. Tasks are
- general-purpose work items that aren't specific to software development or
- software projects.
- - Merge request:
- A work item that includes a proposal or request to apply some specific
- changes to a specific [=Repository=]. The work requested by the work item
- is to review and (decide whether to) merge the proposed patches to the
- repository. This kind of work item is often called "Pull Request" or "Merge
- Request" on software project hosting platforms.
- ### Task / Issue
- A task is represented as a [[#Ticket]] as described above, with the
- following additional requirements:
- - [=context=] is the [=TicketTracker=] to which this task belongs
- - There is no [=attachment=] of type [=Offer=] (but there may be attachment of other types)
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/game-of-life/issues/107",
- "type": "Ticket",
- "context": "https://dev.example/aviva/game-of-life",
- "attributedTo": "https://forge.example/luke",
- "summary": "Window title is empty",
- "content": "<p>When I start the simulation, window title disappears suddenly</p>",
- "mediaType": "text/html",
- "source": {
- "mediaType": "text/markdown; variant=Commonmark",
- "content": "When I start the simulation, window title disappears suddenly",
- },
- "published": "2019-11-04T07:00:04.465807Z",
- "followers": "https://dev.example/aviva/game-of-life/issues/107/followers",
- "team": "https://dev.example/aviva/game-of-life/issues/107/team",
- "replies": "https://dev.example/aviva/game-of-life/issues/107/discussion",
- "history": "https://dev.example/aviva/game-of-life/issues/107/activity",
- "dependants": "https://dev.example/aviva/game-of-life/issues/107/rdeps",
- "dependencies": "https://dev.example/aviva/game-of-life/issues/107/deps",
- "isResolved": true,
- "resolvedBy": "https://code.example/martin",
- "resolved": "2020-02-07T06:45:03.281314Z"
- }
- </xmp>
- </div>
- ### Merge Request / Pull Request
- A merge request is represented as a [[#Ticket]] as described above, with
- the following additional requirements:
- - [=context=] is the [=PatchTracker=] to which this merge request belongs
- - There is no [=attachment=] of type [=Offer=] (but there may be attachment of other types)
- - There is exactly one [=attachment=] of type [=Offer=], as described below
- - There MAY be more [=attachment=]s, but they MUST NOT be of type [=Offer=]
- In that special [=attachment=] of type [=Offer=]:
- - [=type=] is [=Offer=]
- - [=origin=] is the [=Repository=] or [=Branch=] from which the proposed changes are proposed to be merged into the target repository/branch
- - [=target=] is the [=Repository=] or [=Branch=] into which the changes are proposed to be merged
- - [=object=] is an [=OrderedCollection=] of [=Patch=]es in reverse
- chronological order, in which, in addition to standard [=OrderedCollection=]
- properties:
- - [=context=] is (the [=id=] of) the [[#Ticket]]
- - [=previousVersions=] is a list of previous versions
- of the merge request's proposed changes, i.e. previous versions of this
- [=OrderedCollection=]; each of those uses
- [=currentVersion=] to point back to this latest
- version
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/game-of-life/pulls/825",
- "type": "Ticket",
- "context": "https://dev.example/aviva/game-of-life",
- "attributedTo": "https://forge.example/luke",
- "summary": "Fix the empty window title bug",
- "content": "<p>This fixes the bug making the title disappear</p>",
- "mediaType": "text/html",
- "source": {
- "mediaType": "text/markdown; variant=Commonmark",
- "content": "This fixes the bug making the title disappear",
- },
- "published": "2022-09-15T14:52:00.125987Z",
- "followers": "https://dev.example/aviva/game-of-life/pulls/825/followers",
- "replies": "https://dev.example/aviva/game-of-life/pulls/825/discussion",
- "isResolved": false,
- "attachment": {
- "type": "Offer",
- "origin": {
- "type": "Branch",
- "context": "https://forge.example/luke/game-of-life",
- "ref": "refs/heads/fix-title-bug"
- },
- "target": {
- "type": "Branch",
- "context": "https://dev.example/aviva/game-of-life",
- "ref": "refs/heads/main"
- },
- "object": {
- "id": "https://dev.example/aviva/game-of-life/pulls/825/versions/1",
- "type": "OrderedCollection",
- "totalItems": 1,
- "items": [
- {
- "type": "Patch",
- "attributedTo": "https://forge.example/luke",
- "context": "https://dev.example/aviva/game-of-life/pulls/825/versions/1",
- "mediaType": "application/x-git-patch",
- "content": "From c9ae5f4ff4a330b6e1196ceb7db1665bd4c1..."
- }
- ],
- "context": "https://dev.example/aviva/game-of-life/pulls/825"
- }
- }
- }
- </xmp>
- </div>
- ## Patch
- <pre class=simpledef>
- [=type=]:
- [=Patch=]
- [=attributedTo=]:
- The [=Person=] who has written the patch
- [=context=]:
- An [=OrderedCollection=] representing a sequence of patches, being
- submitted together as a proposed change to a certain [=Repository=], and
- this patch is an item in that collection
- [=content=]:
- A description of the changes that this patch proposes, encoded in the
- format specified by [=mediaType=]
- [=mediaType=]:
- A native patch format used by the Version Control System of the
- [=Repository=] to which the patch is proposed, and in which the [=content=]
- of this patch is encoded
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/game-of-life/pulls/825/versions/1/patches/1",
- "type": "Patch",
- "attributedTo": "https://forge.example/luke",
- "context": "https://dev.example/aviva/game-of-life/pulls/825/versions/1",
- "mediaType": "application/x-git-patch",
- "content": "From c9ae5f4ff4a330b6e1196ceb7db1665bd4c1..."
- }
- </xmp>
- </div>
- # Access Control
- ## Giving Access
- ### Invite ### {#Invite}
- To offer some actor access to a shared resource (such as a repository or a
- ticket tracker), use an ActivityPub [=Invite=] activity.
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Invite=]
- [=actor=]:
- The entity (person, bot, etc.) that is offering access
- [=instrument=]:
- A [=Role=] specifying which operations on the resource are being allowed
- [=target=]:
- The resource, access to which is being given (for example, a repository)
- [=object=]:
- The actor who is being gives access to the resource
- [=capability=]:
- A previously published `Grant`, giving the `actor` permission to invite
- more actors to access the resource
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/outbox/B47d3",
- "type": "Invite",
- "actor": "https://dev.example/aviva",
- "to": [
- "https://dev.example/aviva/followers",
- "https://coding.community/repos/game-of-life",
- "https://coding.community/repos/game-of-life/followers",
- "https://software.site/bob",
- "https://software.site/bob/followers"
- ],
- "instrument": "maintain",
- "target": "https://coding.community/repos/game-of-life",
- "object": "https://software.site/bob",
- "capability": "https://coding.community/repos/game-of-life/outbox/2c53A"
- }
- </xmp>
- </div>
- ### Join ### {#Join}
- To request access to a shared resource, use an ActivityPub [=Join=] activity.
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Join=]
- [=actor=]:
- The entity (person, bot, etc.) that is requesting access
- [=instrument=]:
- A [=Role=] specifying which operations on the resource are being requested
- [=object=]:
- The resource, access to which is being given (for example, a repository)
- [=capability=]:
- *(optional)* A previously published `Grant`, giving the `actor` permission
- to gain access to the resource without the approval of another actor. If
- `capability` isn't provided, the resource won't grant access before someone
- with adequate access approves the Join request.
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://software.site/bob/outbox/c97E3",
- "type": "Join",
- "actor": "https://software.site/bob",
- "to": [
- "https://coding.community/repos/game-of-life",
- "https://coding.community/repos/game-of-life/followers",
- "https://software.site/bob/followers"
- ],
- "instrument": "maintain",
- "object": "https://coding.community/repos/game-of-life",
- "capability": "https://coding.community/repos/game-of-life/outbox/d38Fa"
- }
- </xmp>
- </div>
- ### Grant ### {#Grant}
- To give some actor access to a shared resource, use a ForgeFed
- [=Grant=] activity.
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Grant=]
- [=actor=]:
- The entity (person, bot, etc.) that is giving access
- [=object=]:
- A [=Role=] specifying which operations on the resource are being allowed
- [=context=]:
- The resource, access to which is being given (for example, a repository)
- [=target=]:
- The actor who is being gives access to the resource
- [=fulfills=]:
- The activity that triggered the sending of the `Grant`, such as a related
- `Invite` (another example: if Alice [=Create=]s a new repository, the
- repository may automatically send back a [=Grant=] giving Alice admin
- access, and this Grant's `fulfills` refers to the [=Create=] that Alice
- sent)
- [=result=]:
- A URI that can be used later for verifying that the given access is still
- approved, thus allowing the actor granting the access to revoke it.
- Alternatively, a JSON object where [=id=] is the URI and [=duration=] MAY
- be specified to allow to skip the revocation check if the duration time
- hasn't yet passed since the last check. If [=duration=] is specified, it
- MUST be positive, and specify only an integral number of seconds that is
- less than `2^63`, and no other component.
- [=allows=]:
- Modes of invocation and/or delegation that this `Grant` is meant to be used
- for
- [=delegates=]:
- If this `Grant` is a delegation, i.e. it is passing on some access that it
- has received, `delegates` specifies the parent `Grant` that it has received
- and now passing on
- [=startTime=]:
- *(optional)* The time at which the Grant becomes valid
- [=endTime=]:
- *(recommended)* The time at which the Grant expires
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://coding.community/repos/game-of-life/outbox/9fA8c",
- "type": "Grant",
- "actor": "https://coding.community/repos/game-of-life",
- "to": [
- "https://dev.example/aviva",
- "https://dev.example/aviva/followers",
- "https://coding.community/repos/game-of-life/followers",
- "https://software.site/bob",
- "https://software.site/bob/followers"
- ],
- "object": "maintain",
- "context": "https://coding.community/repos/game-of-life",
- "target": "https://software.site/bob",
- "fulfills": "https://dev.example/aviva/outbox/B47d3",
- "allows": "invoke",
- "endTime": "2023-12-31T23:00:00-08:00"
- }
- </xmp>
- </div>
- ## Canceling Access
- ### Remove ### {#Remove}
- To disable an actor's membership in a shared resource, invalidating their
- access to it, use an ActivityPub [=Remove=] activity.
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Remove=]
- [=actor=]:
- The actor (person, bot, etc.) that is disabling access disabled
- [=object=]:
- The actor whose access to the resource is being taken away
- [=origin=]:
- The resource, access to which is being taken away (for example, a
- repository)
- [=capability=]:
- A previously published `Grant`, giving the `actor` permission to disable
- the [=object=] actor's access to the resource
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/outbox/F941b",
- "type": "Remove",
- "actor": "https://dev.example/aviva",
- "to": [
- "https://dev.example/aviva/followers",
- "https://coding.community/repos/game-of-life",
- "https://coding.community/repos/game-of-life/followers",
- "https://software.site/bob",
- "https://software.site/bob/followers"
- ],
- "origin": "https://coding.community/repos/game-of-life",
- "object": "https://software.site/bob",
- "capability": "https://coding.community/repos/game-of-life/outbox/2c53A"
- }
- </xmp>
- </div>
- ### Leave ### {#Leave}
- To withdraw your consent for membership in a shared resource, invalidating
- your access to it, use an ActivityPub [=Leave=] activity.
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Leave=]
- [=actor=]:
- The actor (person, bot, etc.) that is requesting to disable their own
- access
- [=object=]:
- The resource, access to which is being disabled (for example, a repository)
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://software.site/bob/outbox/d08F4",
- "type": "Leave",
- "actor": "https://software.site/bob",
- "to": [
- "https://coding.community/repos/game-of-life",
- "https://coding.community/repos/game-of-life/followers",
- "https://software.site/bob/followers"
- ],
- "object": "https://coding.community/repos/game-of-life"
- }
- </xmp>
- </div>
- ### Revoke ### {#Revoke}
- Another activity that can be used for disabling access is [=Revoke=].
- While [[#Remove]] and [[#Leave]] are meant for undoing the effects
- of [[#Invite]] and [[#Join]], `Revoke` is provided as an opposite of
- [[#Grant]]. See the Behavior specification for more information about the
- usage of these different activity types in revocation of access to shared
- resources.
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Revoke=]
- [=actor=]:
- The actor (person, bot, etc.) that is revoking access
- [=fulfills=]:
- An activity that triggered the sending of the `Revoke`, such as a related
- `Remove` or `Leave`
- [=object=]:
- specific [[#Grant]] activities being undone, i.e. the access that they
- granted is now disabled and it cannot be used anymore as the [=capability=]
- of activities. Each Grant may either be mentioned by its [=id=] URI, or be
- included as a full object that includes an [[fep-8b32|integrity proof]].
- </pre>
- <div class=example>
- <xmp highlight=json-ld>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://coding.community/repos/game-of-life/outbox/1C0e2",
- "type": "Revoke",
- "actor": "https://coding.community/repos/game-of-life",
- "to": [
- "https://coding.community/repos/game-of-life/followers",
- "https://software.site/bob",
- "https://software.site/bob/followers"
- ],
- "object": "https://coding.community/repos/game-of-life/outbox/9fA8c"
- }
- </xmp>
- </div>
- ### Undo a Grant ### {#undo-grant}
- The Behavior spec describes flows in which the [[#Revoke]] activity is
- used by resources (more accurately, by the actors managing them) to announce
- that they're disabling [[#Grant]]s that they previously sent. To allow for
- a clear distinction, another activity is provided here, for *other* actors to
- *request* the revocation of specific [[#Grant]]s: The ActivityPub [=Undo=]
- activity.
- It's likely that `Grant`s would exist behind-the-scenes in applications, and
- human actors would then use activities such as `Remove` and `Leave` for
- disabling access. But the ability to disable specific `Grant`s may be required
- for ensuring and maintaining system security, therefore `Undo` is provided here
- as well.
- Properties:
- <pre class=simpledef>
- [=type=]:
- [=Undo=]
- [=actor=]:
- The actor (person, bot, etc.) that is revoking access
- [=object=]:
- specific [[#Grant]] activities being undone, i.e. the access that they
- granted is now disabled and it cannot be used anymore as the [=capability=]
- of activities
- [=capability=]:
- A previously published `Grant`, giving the `actor` permission to disable
- the [=object=] actor's access to the resource
- </pre>
- # Vocabulary # {#vocab}
- ## Types
- The base URI of all ForgeFed terms is `https://forgefed.org/ns#`.
- The ForgeFed vocabulary has a JSON-LD context whose URI is
- `https://forgefed.org/ns`. Implementers MUST either include the
- ActivityPub and ForgeFed contexts in their object definitions, or other
- contexts that would result with the ActivityPub and ForgeFed terms being
- assigned they correct full URIs. Implementers MAY include additional contexts
- and terms as appropriate.
- A typical `@context` of a ForgeFed object may look like this:
- <div class=example>
- <xmp highlight=json-ld>
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ]
- </xmp>
- </div>
- ### Activities
- #### Related to access control
- <pre class=simpledef>
- Name: <dfn dfn export>Grant</dfn>
- URI: https://forgefed.org/ns#Grant
- Extends: [=Activity=]
- Description:
- Indicates that [=target=] is being given (by the [=actor=]) access to a
- resource specified by [=context=] under the role/permission specified by
- [=object=].
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=7>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/aviva/outbox/reBGo",
- "type": "Grant",
- "actor": "https://example.dev/aviva",
- "to": [
- "https://example.dev/aviva/followers",
- "https://example.dev/aviva/myproject",
- "https://example.dev/aviva/myproject/followers",
- "https://example.dev/bob",
- "https://example.dev/bob/followers"
- ],
- "object": "write",
- "context": "https://example.dev/aviva/myproject",
- "target": "https://example.dev/bob"
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>Revoke</dfn>
- URI: https://forgefed.org/ns#Revoke
- Extends: [=Activity=]
- Description:
- Indicates that the [=actor=] is canceling [=target=]'s access to a resource
- specified by [=context=] under the role specified by [=instrument=], making
- the [=Grant=] activities specified by [=object=] unusable anymore in other
- activities' [=capability=] field.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=7>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/myproject/outbox/nlTxb",
- "type": "Revoke",
- "actor": "https://example.dev/myproject",
- "to": [
- "https://example.dev/myproject/followers",
- "https://example.dev/users/aviva"
- ],
- "object": "https://example.dev/myproject/outbox/reBGo",
- "instrument": "https://example.dev/roles/developer",
- "context": "https://example.dev/myproject",
- "target": "https://example.dev/users/aviva"
- }
- </xmp>
- </div>
- #### Related to repositories
- <pre class=simpledef>
- Name: <dfn dfn export>Push</dfn>
- URI: https://forgefed.org/ns#Push
- Extends: [=Activity=]
- Description:
- Indicates that new content has been pushed to the [=Repository=].
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=7>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/aviva/myproject/outbox/reBGo",
- "type": "Push",
- "actor": "https://example.dev/aviva/myproject",
- "attributedTo": "https://example.dev/aviva",
- "to": [
- "https://example.dev/aviva",
- "https://example.dev/aviva/followers",
- "https://example.dev/aviva/myproject/followers"
- ],
- "summary": "<p>Aviva pushed a commit to myproject</p>",
- "object": {
- "type": "OrderedCollection",
- "totalItems": 1,
- "items": [
- {
- "id": "https://example.dev/aviva/myproject/commits/d96596230322716bd6f87a232a648ca9822a1c20",
- "type": "Commit",
- "attributedTo": "https://example.dev/aviva",
- "context": "https://example.dev/aviva/myproject",
- "hash": "d96596230322716bd6f87a232a648ca9822a1c20",
- "created": "2019-11-03T13:43:59Z",
- "summary": "Provide hints in sign-up form fields",
- }
- ]
- },
- "target": "https://example.dev/aviva/myproject/branches/master"
- }
- </xmp>
- </div>
- ### Actors
- #### Software development components
- <pre class=simpledef>
- Name: <dfn dfn export>Repository</dfn>
- URI: https://forgefed.org/ns#Repository
- Extends: [=Object=]
- Description:
- Represents a version control system repository.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=8>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v1",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/treesim",
- "type": "Repository",
- "publicKey": {
- "id": "https://dev.example/aviva/treesim#main-key",
- "owner": "https://dev.example/aviva/treesim",
- "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
- },
- "inbox": "https://dev.example/aviva/treesim/inbox",
- "outbox": "https://dev.example/aviva/treesim/outbox",
- "followers": "https://dev.example/aviva/treesim/followers",
- "team": "https://dev.example/aviva/treesim/team",
- "name": "Tree Growth 3D Simulation",
- "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>"
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>TicketTracker</dfn>
- URI: https://forgefed.org/ns#TicketTracker
- Extends: [=Object=]
- Description:
- Represents a [[wikipedia-ticket-tracker|ticket tracker]], i.e. a project
- managing a list of work items.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=8>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v2",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/treesim",
- "type": ["Repository", "TicketTracker"],
- "publicKey": {
- "id": "https://dev.example/aviva/treesim#main-key",
- "owner": "https://dev.example/aviva/treesim",
- "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
- },
- "inbox": "https://dev.example/aviva/treesim/inbox",
- "outbox": "https://dev.example/aviva/treesim/outbox",
- "followers": "https://dev.example/aviva/treesim/followers",
- "name": "Tree Growth 3D Simulation",
- "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>"
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>PatchTracker</dfn>
- URI: https://forgefed.org/ns#PatchTracker
- Extends: [=Object=]
- Description:
- Represents a tracker of [[wikipedia-pr|merge requests]], i.e. a project
- managing a list of patches or branches submitted as proposed changes to a
- given [=Repository=].
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=8>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v2",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/treesim",
- "type": ["Repository", "TicketTracker", "PatchTracker"],
- "publicKey": {
- "id": "https://dev.example/aviva/treesim#main-key",
- "owner": "https://dev.example/aviva/treesim",
- "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
- },
- "inbox": "https://dev.example/aviva/treesim/inbox",
- "outbox": "https://dev.example/aviva/treesim/outbox",
- "followers": "https://dev.example/aviva/treesim/followers",
- "name": "Tree Growth 3D Simulation",
- "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>"
- }
- </xmp>
- </div>
- #### Organizational structure tools
- <pre class=simpledef>
- Name: <dfn dfn export>Project</dfn>
- URI: https://forgefed.org/ns#Project
- Extends: [=Object=]
- Description:
- Represents a project, a planned endeavor that involves usage of tools
- related to the software development lifecycle. It may be a software
- project, but may also be totally unrelated to software development. For
- example, it may be a book that is being written using Markdown files kept
- in a Git repository. A [=Project=] object is a way to collect forge related
- components together under one title.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=8>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v2",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/projects/wanderer",
- "type": "Project",
- "name": "Wanderer",
- "summary": "3D nature exploration game",
- "components": {
- "type": "Collection",
- "totalItems": 7,
- "items": [
- "https://dev.example/repos/opengl-vegetation",
- "https://dev.example/repos/opengl-vegetation/patch-tracker",
- "https://dev.example/repos/treesim",
- "https://dev.example/repos/treesim/patch-tracker",
- "https://dev.example/repos/wanderer",
- "https://dev.example/repos/wanderer/patch-tracker",
- "https://dev.example/issue-trackers/wanderer"
- ]
- },
- "publicKey": {
- "id": "https://dev.example/projects/wanderer#main-key",
- "owner": "https://dev.example/projects/wanderer",
- "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
- },
- "inbox": "https://dev.example/projects/wanderer/inbox",
- "outbox": "https://dev.example/projects/wanderer/outbox",
- "followers": "https://dev.example/projects/wanderer/followers"
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>Team</dfn>
- URI: https://forgefed.org/ns#Team
- Extends: [=Object=]
- Description:
- Represents a group of people working together, collaborating on shared
- resources. Each member [=Person=] in the team has a defined role, affecting
- the level of access they have to the team's shared resources and to
- managing the team itself.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=8>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v2",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/teams/mobilizon-dev-team",
- "type": "Team",
- "name": "Mobilizon Development Team",
- "summary": "We're creating a federated tool for organizing events!",
- "members": {
- "type": "Collection",
- "totalItems": 3,
- "items": [
- { "type": "Relationship",
- "subject": "https://dev.example/teams/mobilizon-dev-team",
- "relationship": "hasMember",
- "object": "https://dev.example/people/alice",
- "tag": "https://roles.example/admin"
- },
- { "type": "Relationship",
- "subject": "https://dev.example/teams/mobilizon-dev-team",
- "relationship": "hasMember",
- "object": "https://dev.example/people/bob",
- "tag": "maintain"
- },
- { "type": "Relationship",
- "subject": "https://dev.example/teams/mobilizon-dev-team",
- "relationship": "hasMember",
- "object": "https://dev.example/people/celine",
- "tag": "develop"
- }
- ]
- },
- "subteams": {
- "type": "Collection",
- "totalItems": 2,
- "items": [
- "https://dev.example/teams/mobilizon-backend-team",
- "https://dev.example/teams/mobilizon-frontend-team"
- ]
- },
- "context": "https://dev.example/teams/framasoft-developers",
- "publicKey": {
- "id": "https://dev.example/teams/mobilizon-dev-team#main-key",
- "owner": "https://dev.example/teams/mobilizon-dev-team",
- "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
- },
- "inbox": "https://dev.example/teams/mobilizon-dev-team/inbox",
- "outbox": "https://dev.example/teams/mobilizon-dev-team/outbox",
- "followers": "https://dev.example/teams/mobilizon-dev-team/followers"
- }
- </xmp>
- </div>
- ### Objects
- <pre class=simpledef>
- Name: <dfn dfn export>CapabilityUsage</dfn>
- URI: https://forgefed.org/ns#CapabilityUsage
- Extends: [=Object=]
- Values:
- This specification defines 3: [=gatherAndConvey=], [=distribute=] and
- [=invoke]
- Description:
- Represents a mode of using a [=Grant=] as an Object Capability (OCAP).
- There are two conceptual operations for `Grant`s: Invocation (acting
- on the resource under the specified role) and Delegation (passing on the
- access to more actors, possibly with reduced privileges). A value of this
- type refers to one or both of these operations, and possibly to more
- specific conditions and restrictions on applying them.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=7>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>Role</dfn>
- URI: https://forgefed.org/ns#Role
- Extends: [=Object=]
- Values:
- [=visit=], [=report=], [=triage=], [=write=], [=maintain=], [=admin=],
- [=delegate=]
- Description:
- Represents a role that an actor has within a [=Team=], or a role defining
- the level of access an actor has to a resource.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=7>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>Branch</dfn>
- URI: https://forgefed.org/ns#Branch
- Extends: [=Object=]
- Description:
- Represents a named variable reference to a version of the [=Repository=],
- typically used for committing changes in parallel to other development, and
- usually eventually merging the changes into the main history line.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=7>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/luke/myrepo/branches/master",
- "type": "Branch",
- "name": "master",
- "context": "https://example.dev/luke/myrepo",
- "ref": "refs/heads/master"
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>Commit</dfn>
- URI: https://forgefed.org/ns#Commit
- Extends: [=Object=]
- Description:
- Represents a named set of changes in the history of a [=Repository=]. This
- is called "commit" in Git, Mercurial and Monotone; "patch" in Darcs;
- sometimes called "change set". Note that `Commit` is a set of changes that
- already exists in a repo's history, while a [=Patch=] is a separate
- proposed change set, that *could* be applied and pushed to a repo,
- resulting with a `Commit`.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=7>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/alice/myrepo/commits/109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
- "type": "Commit",
- "context": "https://example.dev/alice/myrepo",
- "attributedTo": "https://example.dev/bob",
- "committedBy": "https://example.dev/alice",
- "hash": "109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
- "summary": "Add an installation script, fixes issue #89",
- "description": {
- "mediaType": "text/plain",
- "content": "It's about time people can install on their computers!"
- },
- "created": "2019-07-11T12:34:56Z",
- "committed": "2019-07-26T23:45:01Z"
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>Patch</dfn>
- URI: https://forgefed.org/ns#Patch
- Extends: [=Object=]
- Description:
- Represents a named set of changes that are being proposed for applying to a
- specific [=Repository=].
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=7>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/game-of-life/pulls/825/versions/1/patches/1",
- "type": "Patch",
- "attributedTo": "https://forge.example/luke",
- "context": "https://dev.example/aviva/game-of-life/pulls/825/versions/1",
- "mediaType": "application/x-git-patch",
- "content": "From c9ae5f4ff4a330b6e1196ceb7db1665bd4c1..."
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>TicketDependency</dfn>
- URI: https://forgefed.org/ns#TicketDependency
- Extends: [=Relationship=]
- Description:
- Represents a relationship between 2 [=Ticket=]s, in which the resolution of
- one ticket requires the other ticket to be resolved too. It MUST specify
- the [=subject=], [=object=] and [=relationship=] properties, and the
- `relationship` property MUST be [=dependsOn=].
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=6>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "type": ["Relationship", "TicketDependency"],
- "id": "https://example.dev/ticket-deps/2342593",
- "attributedTo": "https://example.dev/alice",
- "summary": "Alice's ticket depends on Bob's ticket",
- "published": "2019-07-11T12:34:56Z",
- "subject": "https://example.dev/alice/myproj/issues/42",
- "relationship": "dependsOn",
- "object": "https://dev.community/bob/coolproj/issues/85"
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>Ticket</dfn>
- URI: https://forgefed.org/ns#Ticket
- Extends: [=Object=]
- Description:
- Represents an item that requires work or attention. Tickets exist in the
- context of a project (which may or may not be a version-control
- repository), and are used to track ideas, proposals, tasks, bugs and more.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=6>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "type": "Ticket",
- "id": "https://example.dev/alice/myrepo/issues/42",
- "context": "https://example.dev/alice/myrepo",
- "attributedTo": "https://dev.community/bob",
- "summary": "Nothing works!",
- "content": "<p>Please fix. <i>Everything</i> is broken!</p>",
- "mediaType": "text/html",
- "source": {
- "content": "Please fix. *Everything* is broken!",
- "mediaType": "text/markdown; variant=CommonMark"
- },
- "assignedTo": "https://example.dev/alice",
- "isResolved": false
- }
- </xmp>
- </div>
- ## Properties
- ### General-purpose
- <pre class=simpledef>
- Name: <dfn dfn export>earlyItems</dfn>
- URI: https://forgefed.org/ns#earlyItems
- Domain: [=OrderedCollection=]
- Range: Ordered list of [ [=Object=] | [=Link=] ]
- Functional: No
- Inverse of: None
- Description:
- In an ordered collection (or an ordered collection page) in which [=items=]
- (or [=orderedItems=]) contains a continuous subset of the collection's
- items from one end, `earlyItems` identifiers a continuous subset from the
- other end. For example, if `items` lists the chronologically latest items,
- `earlyItems` would list the chrologically earliest items. The ordering rule
- for items in `earlyItems` MUST be the same as in `items`. For examle, if
- `items` lists items in reverse chronogical order, then so does
- `earlyItems`.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=14>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/outbox",
- "type": "OrderedCollection",
- "totalItems": 712,
- "orderedItems": [
- "https://dev.example/aviva/outbox/712",
- "https://dev.example/aviva/outbox/711",
- "https://dev.example/aviva/outbox/710"
- ],
- "earlyItems": [
- "https://dev.example/aviva/outbox/3",
- "https://dev.example/aviva/outbox/2",
- "https://dev.example/aviva/outbox/1"
- ]
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>previousVersions</dfn>
- URI: https://forgefed.org/ns#previousVersions
- Domain: [=Object=]
- Range: `rdf:List` of objects of the same `@type` as the subject
- Functional: Yes
- Inverse of: None, but see [=currentVersion=]
- Description:
- Specifies the previous versions of the subject, as an ordered list in
- reverse chronological order.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=10>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/notes/107",
- "type": "Note",
- "attributedTo": "https://dev.example/aviva",
- "content": "I agree!!!!! (edit: fixed a typo)",
- "previousVersions": [
- "https://dev.example/aviva/notes/107_old_version",
- "https://dev.example/aviva/notes/107_very_old_version",
- "https://dev.example/aviva/notes/107_ancient_version"
- ]
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>currentVersion</dfn>
- URI: https://forgefed.org/ns#currentVersion
- Domain: [=Object=]
- Range: [=Object=], of the same `@type` as the subject
- Functional: Yes
- Inverse of: None, but see [=previousVersions=]
- Description:
- Specifies the latest. current, up-to-date version of the subject. Once the
- subject specifies the `currentVersion` property, it SHOULD NOT get any
- changes to any other properties. The exception is `currentVersion` itself,
- which MUST be updated whenever needed, to always point to the latest
- version.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=10>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/notes/107_old_version",
- "type": "Note",
- "attributedTo": "https://dev.example/aviva",
- "content": "I agree!!111",
- "currentVersion": "https://dev.example/aviva/notes/107"
- }
- </xmp>
- </div>
- ### Projects
- <pre class=simpledef>
- Name: <dfn dfn export>components</dfn>
- URI: https://forgefed.org/ns#components
- Domain: [=Project=]
- Range: [=Collection=]
- Functional: Yes
- Inverse of:
- None, but see the usage of [=context=] in the Modeling specification, e.g.
- in [[#Repository]]
- Description:
- Identifies a [=Collection=] listing actors whose services and resources are
- considered to be components of this project.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=11>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/projects/wanderer",
- "type": "Project",
- "name": "Wanderer",
- "summary": "3D nature exploration game",
- "components": {
- "type": "Collection",
- "totalItems": 7,
- "items": [
- "https://dev.example/repos/opengl-vegetation",
- "https://dev.example/repos/opengl-vegetation/patch-tracker",
- "https://dev.example/repos/treesim",
- "https://dev.example/repos/treesim/patch-tracker",
- "https://dev.example/repos/wanderer",
- "https://dev.example/repos/wanderer/patch-tracker",
- "https://dev.example/issue-trackers/wanderer"
- ]
- },
- "inbox": "https://dev.example/projects/wanderer/inbox",
- "outbox": "https://dev.example/projects/wanderer/outbox",
- "followers": "https://dev.example/projects/wanderer/followers"
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>subprojects</dfn>
- URI: https://forgefed.org/ns#subprojects
- Domain: [=Project=]
- Range: [=Collection=] of [=Project=]s
- Functional: Yes
- Inverse of:
- None, but see the usage of [=context=] in [[#Project]]
- Description:
- Identifies a [=Collection=] listing the subprojects of this [=Project=].
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=11>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/projects/wanderer",
- "type": "Project",
- "name": "Wanderer",
- "summary": "3D nature exploration game",
- "subprojects": {
- "type": "Collection",
- "totalItems": 2,
- "items": [
- "https://dev.example/projects/nature-3d-models",
- "https://dev.example/projects/wanderer-fundraising"
- ]
- },
- "inbox": "https://dev.example/projects/wanderer/inbox",
- "outbox": "https://dev.example/projects/wanderer/outbox",
- "followers": "https://dev.example/projects/wanderer/followers"
- }
- </xmp>
- </div>
- ### Team membership and nesting
- <pre class=simpledef>
- Name: <dfn dfn export>hasMember</dfn>
- URI: https://forgefed.org/ns#hasMember
- Domain: [=Team=]
- Range: [=Person=]
- Functional: No
- Inverse of: None
- Description:
- Identifier a [=Person=] who is a member of this [=Team=].
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>members</dfn>
- URI: https://forgefed.org/ns#members
- Domain: [=Team=]
- Range:
- [=Collection=] of [=Relationship=]s whose [=relationship=] is [=hasMember=]
- and whose [=subject=] is this `Team`.
- Functional: Yes
- Inverse of: None
- Description:
- Identifies a collection of the members of this [=Team=], represented as
- [=hasMember=] [=Relationship=]s.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>subteams</dfn>
- URI: https://forgefed.org/ns#subteams
- Domain: [=Team=]
- Range: [=Collection=] of [=Team=]s.
- Functional: Yes
- Inverse of: None
- Description:
- Identifies a collection of the subteams of this [=Team=], i.e. teams whose
- members inherit the access that this team's members have to projects and to
- project components (such as [=Repository=]s).
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- ### Ticket assignment and resolution
- <pre class=simpledef>
- Name: <dfn dfn export>assignedTo</dfn>
- URI: https://forgefed.org/ns#assignedTo
- Domain: [=Ticket=]
- Range: [=Person=]
- Functional: Yes
- Inverse of: None
- Description:
- Identifies the [=Person=] assigned to work on this [=Ticket=].
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>isResolved</dfn>
- URI: https://forgefed.org/ns#isResolved
- Domain: [=Ticket=]
- Range: `xsd:boolean`
- Functional: Yes
- Inverse of: None
- Description:
- Specifies whether the [=Ticket=] is closed, i.e. the work on it is done and
- it doesn't need to attract attention anymore.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>resolvedBy</dfn>
- URI: https://forgefed.org/ns#resolvedBy
- Domain: [=Ticket=]
- Range: [=Object=] than is an actor, or [=Activity=]
- Functional: Yes
- Inverse of: None
- Description:
- Identifies the Actor who has resolved the [=Ticket=], or the activity that
- has resolved the Ticket.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>resolved</dfn>
- URI: https://forgefed.org/ns#resolved
- Domain: [=Ticket=]
- Range: `xsd:dateTime`
- Functional: Yes
- Inverse of: None
- Description:
- For a resolved [=Ticket=], specifies the time the Ticket has been resolved.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- ### Ticket dependencies
- <pre class=simpledef>
- Name: <dfn dfn export>dependsOn</dfn>
- URI: https://forgefed.org/ns#dependsOn
- Domain: [=Ticket=]
- Range: [=Ticket=]
- Functional: No
- Inverse of: [=dependedBy=]
- Description:
- Identifies one or more tickets on which this [=Ticket=] depends, i.e. it
- can't be resolved without those tickets being resolved too.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>dependedBy</dfn>
- URI: https://forgefed.org/ns#dependedBy
- Domain: [=Ticket=]
- Range: [=Ticket=]
- Functional: No
- Inverse of: [=dependsOn=]
- Description:
- Identifies one or more tickets which depend on this [=Ticket=], i.e. they
- can't be resolved without this tickets being resolved too.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>dependencies</dfn>
- URI: https://forgefed.org/ns#dependencies
- Domain: [=Ticket=]
- Range: [=Collection=] of items of type [=TicketDependency=]
- Functional: Yes
- Inverse of: None
- Description:
- Identifies a [=Collection=] of [=TicketDependency=] which specify tickets
- that this [=Ticket=] depends on, i.e. this ticket is the [=subject=] of the
- [=dependsOn=] relationship.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>dependants</dfn>
- URI: https://forgefed.org/ns#dependants
- Domain: [=Ticket=]
- Range: [=Collection=] of items of type [=TicketDependency=]
- Functional: Yes
- Inverse of: None
- Description:
- Identifies a [=Collection=] of [=TicketDependency=] which specify tickets
- that depends on this [=Ticket=], i.e. this ticket is the [=object=] of the
- [=dependsOn=] relationship. Often called "reverse dependencies".
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- ### Repository cloning
- <pre class=simpledef>
- Name: <dfn dfn export>cloneUri</dfn>
- URI: https://forgefed.org/ns#cloneUri
- Domain: [=Repository=]
- Range: [=Object=]
- Functional: No
- Inverse of: None
- Description:
- An endpoint that can be used with a VCS protocol such as Git or Mercurial
- to access a given repository.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- ### Repository pushing
- <pre class=simpledef>
- Name: <dfn dfn export>hashBefore</dfn>
- URI: https://forgefed.org/ns#hashBefore
- Domain: [=Push=]
- Range: `xsd:string` of hexadecimal digit ASCII characters
- Functional: Yes
- Inverse of: None
- Description:
- Specifies the hash of the commit that the pushed branch/repo was pointing
- to *right before* the push happpened.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>hashAfter</dfn>
- URI: https://forgefed.org/ns#hashAfter
- Domain: [=Push=]
- Range: `xsd:string` of hexadecimal digit ASCII characters
- Functional: Yes
- Inverse of: None
- Description:
- Specifies the hash of the commit that the pushed branch/repo was pointing
- to *right after* the push happpened.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- ### Commits and branches
- <pre class=simpledef>
- Name: <dfn dfn export lt="prop-repository">repository</dfn> (DEPRECATED)
- URI: https://forgefed.org/ns#repository
- Domain: [=Commit=]
- Range: [=Repository=]
- Functional: Yes
- Inverse of: None
- Description:
- Identifies the repository to which a commit belongs. DEPRECATED: Use the
- standard ActivityPub `context` property instead.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>description</dfn>
- URI: https://forgefed.org/ns#description
- Domain: [=Commit=]
- Range:
- [=Object=], specifying [=content=] and [=mediaType=]. The `mediaType`
- SHOULD be `"text/plain"`.
- Functional: Yes
- Inverse of: None
- Description:
- Specifies the description text of a [=Commit=], which is an optional
- possibly multi-line text provided in addition to the one-line commit title.
- The range of the `description` property works the same way the range of the
- ActivityPub [=source=] property works.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=14>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/alice/myrepo/commits/109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
- "type": "Commit",
- "context": "https://example.dev/alice/myrepo",
- "attributedTo": "https://example.dev/bob",
- "hash": "109ec9a09c7df7fec775d2ba0b9d466e5643ec8c",
- "created": "2019-07-11T12:34:56Z",
- "summary": "Add an installation script, fixes issue #89",
- "description": {
- "mediaType": "text/plain",
- "content": "It's about time people can install on their computers!"
- },
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>committedBy</dfn>
- URI: https://forgefed.org/ns#committedBy
- Domain: [=Commit=]
- Range: [=Object=]
- Functional: Yes
- Inverse of: None
- Description:
- Identifies the actor (usually a person, but could be something else, e.g. a
- bot) that added a set of changes to the version-control [=Repository=].
- Sometimes the author of the changes and the committer of those changes
- aren't the same actor, in which case the `committedBy` property can be used
- to specify who added the changes to the repository. For example, when
- applying a patch to a repository, e.g. a Git repository, the author would
- be the person who made the patch, and the committer would be the person who
- applied the patch to their copy of the repository.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>hash</dfn>
- URI: https://forgefed.org/ns#hash
- Domain: [=Commit=]
- Range: `xsd:string` of hexadecimal digit ASCII characters
- Functional: Yes
- Inverse of: None
- Description:
- Specifies the hash associated with a [=Commit=], which is a unique
- identifier of the commit within the [=Repository=], usually generated as a
- cryptographic hash function of some (or all) of the commit's data or
- metadata. For example, in Git it would be the SHA1 hash of the commit; in
- Darcs it would be the SHA1 hash of the patch info.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>committed</dfn>
- URI: https://forgefed.org/ns#committed
- Domain: [=Commit=]
- Range: `xsd:dateTime`
- Functional: Yes
- Inverse of: None
- Description:
- Specifies the time that a set of changes was committed into the
- [=Repository=] and became a [=Commit=] in it. This can be different from
- the time the set of changes was produced, e.g. if one person creates a
- patch and sends to another, and the other person then applies the patch to
- their copy of the repository. We call the former event "created" and the
- latter event "committed", and this latter event is specified by the
- `committed` property.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>filesAdded</dfn>
- URI: https://forgefed.org/ns#filesAdded
- Domain: [=Commit=]
- Range: `xsd:string`
- Functional: No
- Inverse of: None
- Description:
- Specifies a filename, as a relative path, relative to the top of the tree
- of files in the [=Repository=], of a file that got added in this
- [=Commit=], and didn't exist in the previous version of the tree.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>filesModified</dfn>
- URI: https://forgefed.org/ns#filesModified
- Domain: [=Commit=]
- Range: `xsd:string`
- Functional: No
- Inverse of: None
- Description:
- Specifies a filename, as a relative path, relative to the top of the tree
- of files in the [=Repository=], of a file that existed in the previous
- version of the tree, and its contents got modified in this [=Commit=].
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>filesRemoved</dfn>
- URI: https://forgefed.org/ns#filesRemoved
- Domain: [=Commit=]
- Range: `xsd:string`
- Functional: No
- Inverse of: None
- Description:
- Specifies a filename, as a relative path, relative to the top of the tree
- of files in the [=Repository=], of a file that existed in the previous
- version of the tree, and got removed from the tree in this [=Commit=].
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>ref</dfn>
- URI: https://forgefed.org/ns#ref
- Domain: [=Branch=]
- Range: `xsd:string`
- Functional: Yes
- Inverse of: None
- Description:
- Specifies an identifier for a [=Branch=], that is used in the
- [=Repository=] to uniquely refer to it. For example, in Git,
- "refs/heads/master" would be the `ref` of the master branch.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=11>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/luke/myrepo/branches/master",
- "type": "Branch",
- "name": "master",
- "context": "https://example.dev/luke/myrepo",
- "ref": "refs/heads/master"
- }
- </xmp>
- </div>
- ### Activity addressing
- <pre class=simpledef>
- Name: <dfn dfn export lt="prop-team">team</dfn>
- URI: https://forgefed.org/ns#team
- Domain: [=Object=]
- Range: [=Collection=] of actors
- Functional: Yes
- Inverse of: None
- Description:
- Specifies a [=Collection=] of actors who are working on the object, or
- responsible for it, or managing or administrating it, or having edit access
- to it. For example, for a [=Repository=], it could be the people who have
- push/edit access, the "collaborators" of the repository.
- </pre>
- <div class=example>
- A repository *https://dev.example/aviva/treesim*:
- <xmp highlight=json-ld line-highlight=20>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v1",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/treesim",
- "type": "Repository",
- "publicKey": {
- "id": "https://dev.example/aviva/treesim#main-key",
- "owner": "https://dev.example/aviva/treesim",
- "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
- },
- "inbox": "https://dev.example/aviva/treesim/inbox",
- "outbox": "https://dev.example/aviva/treesim/outbox",
- "followers": "https://dev.example/aviva/treesim/followers",
- "name": "Tree Growth 3D Simulation",
- "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>",
- "team": "https://dev.example/aviva/treesim/team"
- }
- </xmp>
- The repository's team *https://dev.example/aviva/treesim/team*:
- <xmp highlight=json-ld line-highlight=4>
- {
- "@context": "https://www.w3.org/ns/activitystreams",
- "id": "https://dev.example/aviva/treesim/team",
- "type": "Collection",
- "totalItems": 3,
- "items": [
- "https://dev.example/aviva",
- "https://dev.example/luke",
- "https://code.community/users/lorax"
- ]
- }
- </xmp>
- </div>
- ### Tracker linking
- <pre class=simpledef>
- Name: <dfn dfn export>ticketsTrackedBy</dfn>
- URI: https://forgefed.org/ns#ticketsTrackedBy
- Domain: [=Object=]
- Range: [=Object=] that is an actor
- Functional: Yes
- Inverse of: [=tracksTicketsFor=]
- Description:
- Identifies the actor which tracks tickets related to the given object. This
- is the actor to whom you send tickets you'd like to open against the
- object.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=10>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/aviva/treesim",
- "type": "Repository",
- "name": "Tree Growth 3D Simulation",
- "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>",
- "ticketsTrackedBy": "https://bugs.example/projects/treesim"
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>tracksTicketsFor</dfn>
- URI: https://forgefed.org/ns#tracksTicketsFor
- Domain: [=Object=] that is an actor
- Range: [=Object=]
- Functional: No
- Inverse of: [=ticketsTrackedBy=]
- Description:
- Identifies objects for which which this ticket tracker tracks tickets. When
- you'd like to open a ticket against those objects, you can send them to
- this tracker.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=8>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://bugs.example/treesim",
- "type": "Project",
- "tracksTicketsFor": [
- "https://dev.example/aviva/liblsystem",
- "https://dev.example/aviva/3d-tree-models",
- "https://dev.example/aviva/treesim"
- ]
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>sendPatchesTo</dfn>
- URI: https://forgefed.org/ns#sendPatchesTo
- Domain: [=Repository=]
- Range: [=PatchTracker=]
- Functional: Yes
- Inverse of: [=tracksPatchesFor=]
- Description:
- Identifies the [=PatchTracker=] which tracks patches and merge requests
- related to the given repository. This is the actor to whom you send patches
- and merge requests you'd like to open against the repository.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=10>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://dev.example/repos/treesim",
- "type": "Repository",
- "name": "Tree Growth 3D Simulation",
- "summary": "<p>Tree growth 3D simulator for my nature exploration game</p>",
- "sendPatchesTo": "https://bugs.example/pr-trackers/treesim"
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>tracksPatchesFor</dfn>
- URI: https://forgefed.org/ns#tracksPatchesFor
- Domain: [=PatchTracker=]
- Range: [=Repository=]
- Functional: No
- Inverse of: [=sendPatchesTo=]
- Description:
- Identifies a repository for which which this patch and merge request
- tracker tracks patches and merge requests. When you'd like to open patches
- or merge requests against that repository, you can send them to this
- tracker.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=8>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://project.example/treesim",
- "type": "PatchTracker",
- "tracksPatchesFor": [
- "https://dev.example/aviva/liblsystem",
- "https://dev.example/aviva/3d-tree-models",
- "https://dev.example/aviva/treesim"
- ]
- }
- </xmp>
- </div>
- ### Repository forking
- <pre class=simpledef>
- Name: <dfn dfn export>forkedFrom</dfn>
- URI: https://forgefed.org/ns#forkedFrom
- Domain: [=Repository=]
- Range: [=Repository=]
- Functional: Yes
- Inverse of: [=forks=]
- Description:
- Identifies the [=Repository=] which this [=Repository=] was created as a
- fork of, i.e. by cloning it.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=8>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/alice/myfork/",
- "type": "Repository",
- "forkedFrom": {
- "type": "Repository",
- "id": "https://example.dev/luke/myrepo/"
- }
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>forks</dfn>
- URI: https://forgefed.org/ns#forks
- Domain: [=Repository=]
- Range: [=OrderedCollection=] of items of type [=Repository=]
- Functional: Yes
- Inverse of: [=forkedFrom=]
- Description:
- Identifies an [=OrderedCollection=] of [=Repository=]s which were created
- as forks of this [=Repository=], i.e. by cloning it. The order of the
- collection items is by reverse chronological order of the forking events.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=8>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/luke/myrepo/",
- "type": "Repository",
- "forks": {
- "type": "OrderedCollection",
- "totalItems": 1,
- "orderedItems": [
- {
- "id": "https://example.dev/alice/myfork/",
- "type": "Repository",
- }
- ]
- },
- }
- </xmp>
- </div>
- ### Access control
- <pre class=simpledef>
- Name: <dfn dfn export>fulfills</dfn>
- URI: https://forgefed.org/ns#fulfills
- Domain: [=Activity=]
- Range: [=Activity=]
- Functional: No
- Inverse of: None
- Description:
- For an activity *A* that `fulfills` some other activity *B*, specifies that
- *A* has been published as part of fulfilling the action requested by *B*.
- For example, if Alice creates a new repository using `Create`, she may want
- to instantly automatically start following this new repository using
- `Follow` (to be notified when her friends push commits there). This
- `Follow` `fulfills` the `Create`; it's an activity automatically sent as
- part of creating a new repository.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>allows</dfn>
- URI: https://forgefed.org/ns#allows
- Domain: [=Grant=]
- Range: [=CapabilityUsage=]
- Functional: No
- Inverse of: None
- Description:
- Specifies which modes of using this [=Grant=] are being allowd by it. The
- two conceptual operations that `Grant`s support are invocation (acting on
- the resource under the specified role) and delegation (passing on the
- access to more actors, possibly with reduced privileges). This property
- specifies which of these operations are supported, and under which
- conditions. See [=CapabilityUsage=] for specific values to use.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>capability</dfn>
- URI: https://forgefed.org/ns#capability
- Domain: [=Activity=]
- Range: [=Grant=]
- Functional: Yes
- Inverse of: None
- Description:
- Specifies a previously published [=Grant=] activity providing relevant
- access permissions. For example, if Alice wants to resolve a [=Ticket=]
- under some project, she will send an activity with `capability` referring
- to the `Grant` activity that gave her collaborator access to that project,
- which happens to include permission to resolve tickets.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>managedBy</dfn>
- URI: https://forgefed.org/ns#managedBy
- Domain: [=Object=]
- Range: [=Object=] that is an actor
- Functional: Yes
- Inverse of: None
- Description:
- Identifies the actor that controls the given resource, and to whom
- activities asking to modify the resource may be submitted.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "type": "Ticket",
- "id": "https://example.dev/alice/myrepo/issues/42",
- "context": "https://example.dev/alice/myrepo",
- "managedBy": "https://example.dev/alice/myrepo",
- "attributedTo": "https://dev.community/bob",
- "summary": "Nothing works!",
- "content": "<p>Please fix. <i>Everything</i> is broken!</p>",
- "mediaType": "text/html",
- "source": {
- "content": "Please fix. *Everything* is broken!",
- "mediaType": "text/markdown; variant=CommonMark"
- },
- "isResolved": false
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>delegates</dfn>
- URI: https://forgefed.org/ns#delegates
- Domain: [=Grant=]
- Range: [=Grant=]
- Functional: Yes
- Inverse of: None
- Description:
- Actors can use [=Grant=] activities to allow other actors to access their
- resources. They can also allow those other actors to pass on (delegate)
- this access to even more actors. For a `Grant` that delegates access
- provided by an earlier `Grant`, the former uses `delegates` to specify the
- latter. That earlier `Grant` is also called the "parent capability" of this
- `Grant`.
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=9>
- TODO
- </xmp>
- </div>
- ### Repository mirroring
- <pre class=simpledef>
- Name: <dfn dfn export>mirrors</dfn>
- URI: https://forgefed.org/ns#mirrors
- Domain: [=Repository=]
- Range: [=Repository=]
- Functional: Yes
- Inverse of: [=mirroredBy=]
- Description:
- Identifies the [=Repository=] which this [=Repository=] copies content from
- (i.e. what this repository is a "pull mirror" of).
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=8>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/alice/mymirror/",
- "type": "Repository",
- "mirrors": {
- "type": "Repository",
- "id": "https://example.dev/luke/myrepo/"
- }
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>mirroredBy</dfn>
- URI: https://forgefed.org/ns#mirroredBy
- Domain: [=Repository=]
- Range: [=Repository=]
- Functional: No
- Inverse of: [=mirrors=]
- Description:
- Identifies a [=Repository=] which copies content from this repository (i.e.
- "pull mirror" of this repository).
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=8>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/luke/myrepo/",
- "type": "Repository",
- "mirroredBy": {
- "type": "Repository",
- "id": "https://example.dev/alice/mymirror/"
- }
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>mirrorsTo</dfn>
- URI: https://forgefed.org/ns#mirrorsTo
- Domain: [=Repository=]
- Range: [=Repository=]
- Functional: No
- Inverse of: [=mirroredFrom=]
- Description:
- Identifies a [=Repository=] which this repository copies content to (i.e.
- "push mirror" of this repository)
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=8>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/alice/myrepo/",
- "type": "Repository",
- "mirrorsTo": {
- "type": "Repository",
- "id": "https://example.dev/alice-backup/myrepo/"
- }
- }
- </xmp>
- </div>
- <pre class=simpledef>
- Name: <dfn dfn export>mirroredFrom</dfn>
- URI: https://forgefed.org/ns#mirroredFrom
- Domain: [=Repository=]
- Range: [=Repository=]
- Functional: Yes
- Inverse of: [=mirrorsTo=]
- Description:
- Identifies the [=Repository=] which copies its content to this
- [=Repository=] (ie. what this repository is a "push mirror" of).
- </pre>
- <div class=example>
- <xmp highlight=json-ld line-highlight=8>
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://forgefed.org/ns"
- ],
- "id": "https://example.dev/alice-backup/myrepo/",
- "type": "Repository",
- "mirroredFrom": {
- "type": "Repository",
- "id": "https://example.dev/alice/myrepo/"
- }
- }
- </xmp>
- </div>
- ## Values
- ### Capability uses
- <pre class=simpledef>
- Name: <dfn dfn export>gatherAndConvey</dfn>
- URI: https://forgefed.org/ns#gatherAndConvey
- Type: [=CapabilityUsage=]
- Conditions:
- <ul>
- <li>
- The `Grant`'s [=target=] MUST be a [=Project=]
- </li>
- <li>
- It may delegate the `Grant`, allowing only `gatherAndConvey`, to
- parent projects
- </li>
- <li>
- It may delegate the `Grant`, allowing only `distribute`, to teams
- to which it allows to access it
- </li>
- <li>
- It may delegate the `Grant`, allowing `invoke` only, to people to
- which it allows to access it
- </li>
- </ul>
- </pre>
- <pre class=simpledef>
- Name: <dfn dfn export>distribute</dfn>
- URI: https://forgefed.org/ns#distribute
- Type: [=CapabilityUsage=]
- Conditions:
- <ul>
- <li>
- The `Grant`'s [=target=] MUST be a [=Team=]
- </li>
- <li>
- It may delegate the `Grant`, allowing `distribute` only, to its
- subteams
- </li>
- <li>
- It may delegate the `Grant`, allowing `invoke` only, to its members
- </li>
- </ul>
- </pre>
- <pre class=simpledef>
- Name: <dfn dfn export>invoke</dfn>
- URI: https://forgefed.org/ns#invoke
- Type: [=CapabilityUsage=]
- Conditions:
- <ul>
- <li>
- The `Grant`'s [=target=] may invoke it, i.e. use it as the
- [=capability=] in another activity, that requests to access or
- modify the resource specified by the `Grant`'s [=context=]
- </li>
- </ul>
- </pre>
- ### Roles
- <pre class=simpledef>
- Name: <dfn dfn export>visit</dfn>
- URI: https://forgefed.org/ns#visit
- Type: [=Role=]
- Description:
- Authorizes the [=Grant=] recipient (i.e. [=target=]) to view the [=Grant=]
- resource (i.e. [=context=]), which includes retrieving objects via HTTP and
- pulling/cloning VCS repos
- </pre>
- <pre class=simpledef>
- Name: <dfn dfn export>report</dfn>
- URI: https://forgefed.org/ns#report
- Type: [=Role=]
- Description:
- Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
- resource (i.e. [=context=]) anything that the [=visit=] role authorizes,
- and also to do basic community participation tasks:
- [Open an issue](#opening-issue),
- [submit a PR](#opening-mr),
- [create comments](#commenting)
- and discussion threads,
- edit public wikis,
- submit PR reviews.
- </pre>
- <pre class=simpledef>
- Name: <dfn dfn export>triage</dfn>
- URI: https://forgefed.org/ns#triage
- Type: [=Role=]
- Description:
- Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
- resource (i.e. [=context=]) anything that the [=report=] role authorizes,
- and also to edit issue/PR propeties (labels, milestones, due dates, etc.),
- close and reopen issues and PRs, assign and unassign people to issues and
- PRs, request PR reviews, hide disruptive comments (a moderation action),
- lock and move discussions.
- </pre>
- <pre class=simpledef>
- Name: <dfn dfn export>write</dfn>
- URI: https://forgefed.org/ns#write
- Type: [=Role=]
- Description:
- Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
- resource (i.e. [=context=]) anything that the [=triage=] role authorizes,
- and also to
- apply PR suggested changes,
- edit non-public wikis,
- create/edit/delete labels,
- merge a PR,
- [push to VCS repositories](#pushing),
- create/edit/run/cancel CI recipes,
- manage releases,
- publish packages,
- create web IDE coding sessions.
- </pre>
- <pre class=simpledef>
- Name: <dfn dfn export>maintain</dfn>
- URI: https://forgefed.org/ns#maintain
- Type: [=Role=]
- Description:
- Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
- resource (i.e. [=context=]) anything that the [=write=] role authorizes,
- and also to edit project and component descriptions and settings unrelated
- to access, enable/disable components, configure "Pages" publishing of
- static websites from repos, push to repos' protected branches.
- </pre>
- <pre class=simpledef>
- Name: <dfn dfn export>admin</dfn>
- URI: https://forgefed.org/ns#admin
- Type: [=Role=]
- Description:
- Authorizes the [=Grant=] recipient (i.e. [=target=]) to do on the [=Grant=]
- resource (i.e. [=context=]) anything that the [=maintain=] role authorizes,
- and also to
- [manage access to projects, components and teams](#managing-access),
- merge PRs even without reviews,
- delete issues,
- change project/component/team visibility,
- edit project/component/team access-related settings,
- change a repo's default branch,
- manage webhooks and deployment,
- move components and projects between projects,
- archive projects/components,
- delete components/projects/teams.
- </pre>
- <pre class=simpledef>
- Name: <dfn dfn export>delegate</dfn>
- URI: https://forgefed.org/ns#delegate
- Type: [=Role=]
- Description:
- Authorizes the [=Grant=] recipient (i.e. [=target=]) to send access
- delegations to the [=Grant=] sender (i.e. [=actor=])
- </pre>
- <pre class=biblio>
- {
- "wikipedia-ticket-tracker": {
- "href": "https://en.wikipedia.org/wiki/Issue_tracking_system",
- "title": "Wikipedia: Issue tracking system"
- },
- "wikipedia-pr": {
- "href": "https://en.wikipedia.org/wiki/Distributed_version_control#Pull_requests",
- "title": "Wikipedia: Pull requests"
- },
- "fep-8b32": {
- "href": "https://codeberg.org/fediverse/fep/src/branch/main/feps/fep-8b32.md",
- "title": "FEP-8b32: Object Integrity Proofs"
- }
- }
- </pre>
- <pre class=anchors>
- urlPrefix: https://www.w3.org/TR/xmlschema11-2/#; type: dfn; spec: xmlschema11-2
- text: dateTime
- urlPrefix: http://purl.org/dc/terms/; type: dfn; spec: dcterms
- text: created
- urlPrefix: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-; type: dfn; spec: activitystreams-vocabulary
- text: Accept
- text: Add
- text: Create
- text: Invite
- text: Join
- text: Leave
- text: Offer
- text: Reject
- text: Remove
- text: Undo
- urlPrefix: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-; type: dfn; spec: activitystreams-vocabulary
- text: Activity
- text: Collection
- text: Image
- text: Link
- text: Note
- text: Object
- text: OrderedCollection
- text: Person
- urlPrefix: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-; type: dfn; spec: activitystreams-vocabulary
- text: actor
- text: attachment
- text: attributedTo
- text: content
- text: context
- text: duration
- text: endTime
- text: id
- text: inbox
- text: inReplyTo
- text: instrument
- text: items
- text: mediaType
- text: name
- text: object
- text: orderedItems
- text: origin
- text: published
- text: relationship
- text: replies
- text: result
- text: source
- text: startTime
- text: subject
- text: summary
- text: tag
- text: target
- text: to
- text: type
- urlPrefix: https://www.w3.org/TR/activitypub/#; type: dfn; spec: activitypub
- text: followers
- </pre>
|