Heratio Help Center article. Category: Technical.

Heratio AHG Framework - System Flows

Version: 2.8.2 Last Updated: February 2026


0. Heratio Dual-Mode Request Flow

HERATIODUAL-MODEREQUESTFLOWClient(Browser)HTTPRequestNGINXheratio.conf(ifinstalled)StandardHeratioconfig/ingest/*heratio.php/index.php/admin/ahg-*heratio.php/informationobjindex.php/display/*heratio.php/actorindex.php/privacy/*heratio.php/repositoryindex.php/research/*heratio.php/userindex.php(~40pluginpatterns)(allbaseHeratio)HERATIOENTRYPOINTSYMFONYENTRYPOINTheratio.phpindex.php1.Checkkill-switchsfContext::getInstance()2.BootKerneldispatch()3.Middlewarestack(unchangedHeratio)4.RoutedispatchHERATIOMIDDLEWARESYMFONYFILTERCHAIN1.SessionMiddlewaresecurityFilter2.AuthMiddlewareaccessFilter3.SettingsMiddlewarecacheFilter4.CspMiddlewareexecutionFilter5.MetaMiddleware6.LimitsMiddlewareACTIONBRIDGESYMFONYACTIONDispatchestooneof:sfAction->execute()•AhgControllerPropelORM•AhgActions(Blade)sfViewrendering•sfActions(Bridge)RENDERINGRENDERINGBladeRenderersfPHPViewheratio.blade.phplayout.php(masterlayout)(themelayout)8partialsClientHTMLResponseKILL-SWITCH:Remove.heratio_enabledfileALLroutesgotoindex.php

1. Request Processing Flow

HTTPREQUESTFLOWClient(Browser)HTTPRequestGET/informationobject/browseNGINXlocation/{fastcgi_passphp-fpm;}location/plugins/{alias/path/to/plugins/;}PHP-FPM8.3index.phpsfContext::getInstance()dispatch()SYMFONY1.xFRONTCONTROLLER1.Loadconfig/ProjectConfiguration.class.php2.Callsetup()loadPluginsFromDatabase()3.Bootstrapatom-framework4.Queryatom_pluginforenabledplugins5.Enablepluginsvia$this->enablePlugins()ROUTINGapps/qubit/config/routing.ymlpluginrouting.ymlfiles/informationobject/browseinformationobject/browseActionFILTERCHAINsecurityFilter◄ahgSecurityClearancePlugin(checkaccess)VerifiesuserclearancevsrecordclassaccessFilter◄CoreHeratioACL(checkACL)VerifiesgrouppermissionscacheFilterexecutionFilter◄RunstheactionACTIONEXECUTIONplugins/ahgDisplayPlugin/modules/informationobject/actions/browseAction.class.phpclassbrowseActionextendssfAction{publicfunctionexecute($request){//QueryviaPropel(coreHeratio)$records=QubitInformationObject::getAll();//QueryviaLaravel(extensiondata)$conditions=DB::table('condition_assessment')->whereIn('object_id',$ids)->get();//Triggerhooksforpanels$panels=AhgHooks::trigger('browse.panels');}}TEMPLATERENDERINGLayout:plugins/ahgThemeB5Plugin/templates/layout.phpTemplate:plugins/ahgDisplayPlugin/modules/.../browseSuccess.phpTemplateincludes:•Sector-specificlabelsviaAhgSectorProfile::getLabel()•RegisteredpanelsviaAhgPanels::forPosition()•CapabilitychecksviaAhgCapabilities::has()Client◄HTMLResponse

2. Plugin Installation Flow

PLUGININSTALLATIONFLOW$phpbin/atomextension:installahgPrivacyPlugin1.ExtensionManager::install()Checkifpluginexistslocallyif(!file_exists($pluginsPath/$name)){//FetchfromGitHubviaPluginFetcher$fetcher->clone($repoUrl,$pluginsPath);}2.Readextension.json{"name":"ahgPrivacyPlugin","version":"1.2.0","category":"compliance","dependencies":["ahgCorePlugin"],"database":{"install":"database/install.sql","migrations":"database/migrations/"}}3.Checkdependenciesforeach($dependenciesas$dep){if(!$this->isEnabled($dep)){$this->enable($dep);//Enabledependencyfirst}}4.RundatabasemigrationsMigrationHandler::runInstall($plugin)//Executedatabase/install.sqlCREATETABLEprivacy_breach(...);CREATETABLEprivacy_consent(...);CREATETABLEprivacy_sar_request(...);//Runmigrationsinorder001_initial.sql002_add_breach_columns.sql5.Createsymlink(ifnotexists)ln-s/path/to/atom-ahg-plugins/ahgPrivacyPlugin/path/to/atom/plugins/ahgPrivacyPlugin6.Registerinatom_plugintableINSERTINTOatom_plugin(name,class_name,version,category,is_enabled,is_core,is_locked,load_order)VALUES('ahgPrivacyPlugin','ahgPrivacyPluginConfiguration','1.2.0','compliance',1,0,0,50);7.ClearSymfonycacherm-rfcache/*phpsymfonycc✓PluginahgPrivacyPlugininstalledandenabledRestartPHP-FPMforchangestotakeeffect:$sudosystemctlrestartphp8.3-fpm

3. Audit Trail Flow

AUDITTRAILFLOWUserAction:EditrecordPOST/informationobject/edit/123Action:editAction.class.php//Getoriginalvalues$original=$record->toArray();//Applychanges$record->title=$request->getParameter('title');$record->save();//Calculatediff$changes=array_diff_assoc($record->toArray(),$original);AhgHooks::trigger('record.updated',$record,$changes)ahgAuditTrailPluginlistenerAhgHooks::register('record.updated',function($record,$changes){//CreateauditlogentryDB::table('audit_log')->insert(['user_id'=>sfContext::getInstance()->getUser()->getId(),'object_id'=>$record->id,'object_type'=>get_class($record),'action'=>'update','module'=>'informationobject','changes'=>json_encode($changes),'ip_address'=>$_SERVER['REMOTE_ADDR'],'user_agent'=>$_SERVER['HTTP_USER_AGENT'],'created_at'=>date('Y-m-dH:i:s'),]);//Createdetailrecordsforeachchangedfieldforeach($changesas$field=>$value){DB::table('audit_log_detail')->insert([...]);}});DatabaseStateaudit_logiduser_idobject_idactionchanges425123update{"title":{"old":"...audit_log_detailidaudit_log_idfield_nameold_valuenew_value9842titleOldTitleNewTitleAuditLogViewer:AdminAuditTrailFilter:[User][Action][Module][DateRange][Search]TimeUserActionRecordChanges10:45:23jsmithUPDATEDocumentABC-123title,date10:42:11adminCREATECollectionXYZ-10:38:05jsmithVIEWPhotoAlbum-

4. IIIF Manifest Generation Flow

IIIFMANIFESTGENERATIONFLOWRequest:GET/iiif/manifest/1231.Checkcache$cached=DB::table('iiif_manifest')->where('object_id',123)->where('updated_at','>',$record->updated_at)->first();if($cached)returnjson_decode($cached->manifest_json);Cachemiss2.Loadrecordanddigitalobjects$record=QubitInformationObject::getById(123);$digitalObjects=$record->getDigitalObjects();3.BuildIIIFPresentation3.0manifest{"@context":"http://iiif.io/api/presentation/3/context.json","id":"https://example.org/iiif/manifest/123","type":"Manifest","label":{"en":["RecordTitle"]},"metadata":[{"label":{"en":["Creator"]},"value":{"en":["JohnDoe"]}}],"items":[{"id":"https://example.org/iiif/canvas/123-1","type":"Canvas","width":4000,"height":3000,"items":[...]}]}4.Addannotations(ifavailable)//OCRtext$ocr=DB::table('iiif_ocr_text')->where('object_id',123)->first();if($ocr){manifest.annotations=OcrService::generateAnnotationPage();}//Transcriptions$transcription=TranscriptionService::get(123);if($transcription){manifest.supplementing=...}5.Applyaccessrestrictionsif(AhgCapabilities::has('security')){$classification=SecurityService::getClassification(123);if($classification->level0){manifest.services=[{"@type":"AuthCookieService1",...}];}}6.CacheandreturnDB::table('iiif_manifest')->updateOrInsert(['object_id'=>123],['manifest_json'=>json_encode($manifest),...]);returnResponse::json($manifest)->header('Access-Control-Allow-Origin','*')->header('Content-Type','application/ldjson');ViewerIntegration:<divid="mirador"><script>Mirador.viewer({id:'mirador',windows:[{manifestId:'https://example.org/iiif/manifest/123'}]});</script></div>

5. AI/NER Processing Flow

NERPROCESSINGFLOWTrigger:Userclicks"ExtractEntities"orbatchjobruns1.Collecttextcontent$text=$record->getScopeAndContent().$record->getArchivalHistory().$record->getTitle();2.SendtoPythonNERservice//atom-ahg-python/src/atom_ahg/resources/ner.pyimportspacynlp=spacy.load("en_core_web_lg")doc=nlp(text)entities=[]forentindoc.ents:entities.append({'text':ent.text,'type':ent.label_,#PERSON,ORG,GPE,DATE'start':ent.start_char,'end':ent.end_char,'confidence':ent.kb_id_})3.ProcessentitiesEntitiesfound:TextTypeActionJohnSmithPERSONLinkto/createactorrecordAcmeCorporationORGLinkto/createactorrecordCapeTownGPELinktoplaceauthority15March1952DATEParseandvalidatedate4.Userreview(optional)EntitySuggestions☑JohnSmith(PERSON)Createnewactor☑AcmeCorporation(ORG)Linktoexisting:AcmeCorpLtd☐CapeTown(PLACE)[Reject-alreadylinked]☑15March1952(DATE)Addtodatesfield[ApplySelected][RejectAll]5.Applychanges//Createactorifnotexists$actor=QubitActor::getByName('JohnSmith')??QubitActor::create(['authorized_form_of_name'=>'JohnSmith']);//LinktorecordQubitRelation::create(['subject_id'=>$record->id,'object_id'=>$actor->id,'type_id'=>QubitTerm::NAME_ACCESS_POINT_ID]);//LogextractionDB::table('ner_extraction_log')->insert([...]);

6. Ingest Pipeline Flow

INGESTPIPELINEFLOWSTEP1STEP2STEP3STEP4STEP5CONFIGURE►UPLOAD►MAP&ENRICH►VALIDATE►PREVIEW(sector,(CSV/ZIP/(auto-map,(required(treeview,standard,EAD,dir)metadata,fields,approval)options)profiles)checksums)►STEP6COMMITCOMMITFLOW(BackgroundJob)BrowserPOST►Action►Createingest_job(status=queued)►nohupphpsymfonyingest:commitjob-id=X&►Returnpage(AJAXpollingstarts)CLITask►Markrunning►Foreachrow:CreateInformationObjectCreateDigitalObject(ifDOpath)GeneratederivativesRunAIprocessing(NER/OCR/etc)►BuildSIPpackage►BuildAIP►BuildDIP►Updatesearchindex►GeneratemanifestCSV►MarkcompletedBrowser◄pollevery2s►/ingest/ajax/job-status?job_id=X►Showprogressbar►Oncomplete:showreportcardROLLBACK:DeletescreatedIOsDOspackages,restoressessionstate

Part of the Heratio AHG Framework - v2.8.2