--- include/rpm/rpmcli.h.orig	2025-02-19 15:29:33.000000000 +0000
+++ include/rpm/rpmcli.h	2025-04-14 13:20:00.048365266 +0000
@@ -306,6 +306,7 @@ enum rpmInstallFlags_e {
     INSTALL_ALLMATCHES	= (1 << 9),	/*!< from --allmatches */
     INSTALL_REINSTALL	= (1 << 10),	/*!< from --reinstall */
     INSTALL_RESTORE	= (1 << 11),	/*!< from --restore */
+    INSTALL_RUNPOSTTRANS = (1 << 12),	/*!< from --runposttrans */
 };
 
 typedef rpmFlags rpmInstallFlags;
@@ -396,6 +397,15 @@ int rpmErase(rpmts ts, struct rpmInstall
 int rpmRestore(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_const_t argv);
 
 /** \ingroup rpmcli
+ * Run posttrans scriptlets
+ * @param ts		transaction set
+ * @param ia		control args/bits
+ * @param argv		array of trigger manifest file names (NULL terminated)
+ * @return		0 on success
+ */
+int rpmRunPostTrans(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_const_t argv);
+
+/** \ingroup rpmcli
  */
 extern struct rpmInstallArguments_s rpmIArgs;
 
--- include/rpm/rpmts.h.orig	2025-04-14 13:19:55.520372147 +0000
+++ include/rpm/rpmts.h	2025-04-14 13:20:00.052365260 +0000
@@ -253,6 +253,15 @@ int rpmtsOrder(rpmts ts);
 int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet);
 
 /** \ingroup rpmts
+ * Run all posttrans scriptlets described in the manifest data.
+ *       
+ * @param ts		transaction set
+ * @param manifest	the manifest data
+ * @return		0 on success, -1 on error
+ */
+int rpmtsRunPostTrans(rpmts ts, ARGV_const_t manifest);
+
+/** \ingroup rpmts
  * Reference a transaction set instance.
  * @param ts		transaction set
  * @return		new transaction set reference
--- lib/poptI.c.orig	2025-02-19 15:29:33.000000000 +0000
+++ lib/poptI.c	2025-04-14 13:20:00.052365260 +0000
@@ -283,6 +283,10 @@ struct poptOption rpmInstallPoptTable[]
 	&rpmIArgs.installInterfaceFlags, (INSTALL_RESTORE),
 	N_("restore package(s)"),
 	N_("<packagefile>+") },
+ { "runposttrans", '\0', POPT_BIT_SET,
+	&rpmIArgs.installInterfaceFlags, INSTALL_RUNPOSTTRANS,
+	N_("run posttrans scriptlet"),
+	N_("<posttransmanifest>") },
 
    POPT_TABLEEND
 };
--- lib/psm.c.orig	2025-04-14 13:19:55.504372171 +0000
+++ lib/psm.c	2025-04-14 13:20:00.052365260 +0000
@@ -1009,7 +1009,7 @@ static rpmRC rpmPackageErase(rpmts ts, r
 	}
 	if (rc) break;
 
-	if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS|RPMTRANS_FLAG_NOTRIGGERPOSTUN))) {
+	if (ts->dump_posttrans || !(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS|RPMTRANS_FLAG_NOTRIGGERPOSTUN))) {
 	    /* Prepare post transaction uninstall triggers */
 	    rpmtriggersPrepPostUnTransFileTrigs(psm->ts, psm->te);
 	}
--- lib/rpminstall.c.orig	2025-02-19 15:29:33.000000000 +0000
+++ lib/rpminstall.c	2025-04-25 09:21:50.799906982 +0000
@@ -6,6 +6,8 @@
 
 #include <string.h>
 
+#include <errno.h>
+
 #include <rpm/rpmcli.h>
 #include <rpm/rpmtag.h>
 #include <rpm/rpmlib.h>		/* rpmReadPackageFile, vercmp etc */
@@ -90,6 +92,24 @@ static rpmVSFlags setvsFlags(struct rpmI
     return vsflags;
 }
 
+static const char *
+posttranstag2str(rpmTagVal stag)
+{
+    switch (stag) {
+    case RPMTAG_POSTTRANS:
+	return "posttrans";
+    case RPMTAG_POSTUNTRANS:
+	return "postuntrans";
+    case RPMTAG_TRIGGERIN:
+	return "transfiletriggerin";
+    case RPMTAG_TRIGGERUN:
+	return "transfiletriggerun";
+    case RPMTAG_TRIGGERPOSTUN:
+	return "transfiletriggerpostun";
+    }
+    return rpmTagGetName(stag);
+}
+
 void * rpmShowProgress(const void * arg,
 			const rpmCallbackType what,
 			const rpm_loff_t amount,
@@ -213,8 +233,20 @@ void * rpmShowProgress(const void * arg,
     case RPMCALLBACK_CPIO_ERROR:
 	break;
     case RPMCALLBACK_SCRIPT_ERROR:
+	if (flags & INSTALL_RUNPOSTTRANS) {
+	    rpmTagVal stag = (rpmTagVal)amount;
+	    char *s = headerGetAsString(h, RPMTAG_NEVRA);
+	    fprintf(stdout, "Error from %%%s(%s)\n", posttranstag2str(stag), s);
+	    free(s);
+	}
 	break;
     case RPMCALLBACK_SCRIPT_START:
+	if (flags & INSTALL_RUNPOSTTRANS) {
+	    rpmTagVal stag = (rpmTagVal)amount;
+	    char *s = headerGetAsString(h, RPMTAG_NEVRA);
+	    fprintf(stdout, "Running %%%s(%s)\n", posttranstag2str(stag), s);
+	    free(s);
+	}
 	break;
     case RPMCALLBACK_SCRIPT_STOP:
 	break;
@@ -831,3 +863,33 @@ int rpmInstallSource(rpmts ts, const cha
     return rc;
 }
 
+int rpmRunPostTrans(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_const_t fileArgv)
+{
+    ARGV_t manifest = NULL;
+    FILE *f;
+    char line[BUFSIZ], *s, *p;
+    int rc = 1;
+
+    if (fileArgv == NULL) return 0;
+    if (!fileArgv[0] || fileArgv[1] != NULL) {
+	rpmlog(RPMLOG_ERR, _("runposttrans needs exactly one manifest file\n"));
+	goto exit;
+    }
+    if (!(f = fopen(fileArgv[0], "r"))) {
+	rpmlog(RPMLOG_ERR, _("cannot open %s: %s\n"), fileArgv[0], strerror(errno));
+	goto exit;
+    }
+    while ((s = fgets(line, sizeof(line) - 1, f)) != 0) {
+	if ((p = strrchr(s, '\n')) != 0)
+	    *p = 0;
+	argvAdd(&manifest, s);
+    }
+    fclose(f);
+    rpmlog(RPMLOG_DEBUG, "running posttrans scriptlets\n");
+    rpmtsClean(ts);
+    setNotifyFlag(ia, ts);
+    rc = rpmtsRunPostTrans(ts, manifest);
+exit:
+    argvFree(manifest);
+    return rc;
+}
--- lib/rpmtriggers.c.orig	2025-02-19 15:29:33.000000000 +0000
+++ lib/rpmtriggers.c	2025-04-14 13:20:00.052365260 +0000
@@ -1,5 +1,6 @@
 #include "system.h"
 
+#include <rpm/rpmlog.h>
 #include <rpm/rpmts.h>
 #include <rpm/rpmdb.h>
 #include <rpm/rpmds.h>
@@ -34,7 +35,7 @@ rpmtriggers rpmtriggersFree(rpmtriggers
     return NULL;
 }
 
-static void rpmtriggersAdd(rpmtriggers trigs, unsigned int hdrNum,
+void rpmtriggersAdd(rpmtriggers trigs, unsigned int hdrNum,
 			    unsigned int tix, unsigned int priority)
 {
     if (trigs->count == trigs->alloced) {
@@ -190,6 +191,14 @@ int runPostUnTransFileTrigs(rpmts ts)
 	if (trigH == NULL)
 	    continue;
 
+	if (ts->dump_posttrans) {
+	    char *trigNEVRA = headerGetAsString(trigH, RPMTAG_NEVRA);
+	    rpmlog(RPMLOG_NOTICE, "dump_posttrans: transfiletriggerpostun %u %u %s\n", trigs->triggerInfo[i].tix, trigs->triggerInfo[i].hdrNum, trigNEVRA);
+	    free(trigNEVRA);
+	    headerFree(trigH);
+	    continue;
+	}
+
 	/* Prepare and run script */
 	script = rpmScriptFromTriggerTag(trigH,
 		triggertag(RPMSENSE_TRIGGERPOSTUN),
@@ -613,6 +622,16 @@ rpmRC runImmedFileTriggers(rpmts ts, rpm
     rpmTagVal priorityTag;
     rpmtriggers triggers;
 
+    if (sense == RPMSENSE_TRIGGERIN && tm == RPMSCRIPT_TRANSFILETRIGGER && ts->dump_posttrans) {
+	unsigned int hdrNum = headerGetInstance(trigH);
+	if (hdrNum) {
+	    char *trigNEVRA = headerGetAsString(trigH, RPMTAG_NEVRA);
+	    rpmlog(RPMLOG_NOTICE, "dump_posttrans: install %u %s\n", hdrNum, trigNEVRA);
+	    free(trigNEVRA);
+	}
+	headerFree(trigH);
+	return RPMRC_OK;
+    }
     if (tm == RPMSCRIPT_FILETRIGGER) {
 	priorityTag = RPMTAG_FILETRIGGERPRIORITIES;
     } else {
--- lib/rpmtriggers.h.orig	2025-02-19 15:29:33.000000000 +0000
+++ lib/rpmtriggers.h	2025-04-14 13:20:00.052365260 +0000
@@ -24,6 +24,10 @@ rpmtriggers rpmtriggersCreate(unsigned i
 RPM_GNUC_INTERNAL
 rpmtriggers rpmtriggersFree(rpmtriggers triggers);
 
+RPM_GNUC_INTERNAL
+void rpmtriggersAdd(rpmtriggers trigs, unsigned int hdrNum,
+			unsigned int tix, unsigned int priority);
+
 /*
  * Prepare post trans uninstall file triggers. After transcation uninstalled
  * files are not saved anywhere. So we need during uninstalation of every
--- lib/rpmts_internal.h.orig	2025-02-19 15:29:33.000000000 +0000
+++ lib/rpmts_internal.h	2025-04-14 13:20:00.052365260 +0000
@@ -83,6 +83,8 @@ struct rpmts_s {
     int min_writes;             /*!< macro minimize_writes used */
 
     time_t overrideTime;	/*!< Time value used when overriding system clock. */
+
+    int dump_posttrans;		/*!< macro dump_posttrans used */
 };
 
 /** \ingroup rpmts
--- lib/transaction.c.orig	2025-02-19 15:29:33.000000000 +0000
+++ lib/transaction.c	2025-04-14 13:20:00.056365254 +0000
@@ -1475,6 +1475,8 @@ static int rpmtsSetup(rpmts ts, rpmprobF
     /* Get available space on mounted file systems. */
     (void) rpmtsInitDSI(ts);
 
+    /* Initialize the dump_posttrans flag */
+    ts->dump_posttrans = (rpmExpandNumeric("%{?_dump_posttrans}") > 0);
     return 0;
 }
 
@@ -1858,6 +1860,15 @@ int rpmtsRun(rpmts ts, rpmps okProbs, rp
     /* Actually install and remove packages */
     nfailed = rpmtsProcess(ts);
 
+    if (ts->dump_posttrans) {
+	rpmlog(RPMLOG_NOTICE, "dump_posttrans: enabled\n");
+	runPostUnTransFileTrigs(ts);
+	runTransScripts(ts, PKG_TRANSFILETRIGGERIN);
+	/* Final exit code */
+	rc = nfailed ? -1 : 0;
+	goto exit;
+    }
+
     /* Run %posttrans scripts unless disabled */
     if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS))) {
 	rpmlog(RPMLOG_DEBUG, "running %%posttrans scripts\n");
@@ -1901,3 +1912,117 @@ exit:
     sigaction(SIGPIPE, &oact, NULL);
     return rc;
 }
+
+static unsigned int runPostTransFindPkgNum(const char **lpp)
+{
+    const char *lp = *lpp;
+    unsigned int num = strtoul(lp, 0, 10);
+    while (*lp >= '0' && *lp <= '9')
+	lp++;
+    while (*lp == ' ')
+	lp++;
+    *lpp = lp;
+    return num;
+}
+
+static Header runPostTransFindPkg(rpmts ts, const char *lp)
+{
+    rpmdbMatchIterator mi;
+    Header h = NULL;
+    unsigned int hdrnum = runPostTransFindPkgNum(&lp);
+    if (!*lp)
+	return NULL;
+    if (hdrnum) {
+	mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &hdrnum, sizeof(hdrnum));
+	h = headerLink(rpmdbNextIterator(mi));
+	rpmdbFreeIterator(mi);
+    }
+    if (h) {
+	char *NEVRA = headerGetAsString(h, RPMTAG_NEVRA);
+	if (!NEVRA || strcmp(NEVRA, lp) != 0)
+	    h = headerFree(h);
+	_free(NEVRA);
+    }
+    if (!h) {
+	mi = rpmtsInitIterator(ts, RPMDBI_LABEL, lp, strlen(lp));
+	h = headerLink(rpmdbNextIterator(mi));
+	rpmdbFreeIterator(mi);
+    }
+    if (!h)
+	rpmlog(RPMLOG_WARNING, "package %s is not installed\n", lp);
+    return h;
+}
+
+int rpmtsRunPostTrans(rpmts ts, ARGV_const_t manifest)
+{
+    int rc = -1; /* assume failure */
+    /* setup */
+    tsMembers tsmem = rpmtsMembers(ts);
+    rpmtxn txn = NULL;
+    /* Ignore SIGPIPE for the duration of transaction */
+    struct sigaction act, oact;
+    memset(&act, 0, sizeof(act));
+    act.sa_handler = SIG_IGN;
+    sigaction(SIGPIPE, &act, &oact);
+    /* Force default 022 umask during transaction for consistent results */
+    mode_t oldmask = umask(022);
+    
+    if (tsmem->orderCount)
+	goto exit;
+    char *line;
+    while ((line = *manifest++) != 0) {
+	if (!strncmp(line, "dump_posttrans: install ", 24)) {
+	    const char *lp = line + 24;
+	    Header h = runPostTransFindPkg(ts, lp);
+	    if (!h)
+		continue;
+	    rpmte p = rpmteNew(ts, h, TR_ADDED, line + 45, NULL, RPMTE_INSTALL);
+	    if (tsmem->orderCount >= tsmem->orderAlloced) {
+		tsmem->orderAlloced += (tsmem->orderCount - tsmem->orderAlloced) + tsmem->delta;
+		tsmem->order = xrealloc(tsmem->order, tsmem->orderAlloced * sizeof(*tsmem->order));
+	    }
+	    tsmem->order[tsmem->orderCount++] = p; 
+
+	    if (tsmem->addedPackages == NULL)
+		tsmem->addedPackages = rpmalCreate(ts, 5);
+	    rpmalAdd(tsmem->addedPackages, p);
+	    packageHashAddEntry(tsmem->installedPackages, headerGetInstance(h), p);
+	} else if (!strncmp(line, "dump_posttrans: transfiletriggerpostun ", 39)) {
+	    const char *lp = line + 39;
+	    unsigned int tix = runPostTransFindPkgNum(&lp);
+	    Header h = runPostTransFindPkg(ts, lp);
+	    struct rpmtd_s priorities;
+	    if (!h)
+		continue;
+	    headerGet(h, RPMTAG_TRANSFILETRIGGERPRIORITIES, &priorities, HEADERGET_MINMEM);
+	    if (rpmtdSetIndex(&priorities, tix) >= 0)
+		rpmtriggersAdd(ts->trigs2run, headerGetInstance(h), tix, *rpmtdGetUint32(&priorities));
+	    headerFree(h);
+	}
+    }
+
+    if (!(txn = rpmtxnBegin(ts, RPMTXN_WRITE)))
+	goto exit;
+
+    if (rpmChrootSet(rpmtsRootDir(ts)))
+	goto exit;
+
+    /* run posttrans scripts */
+    rpmlog(RPMLOG_DEBUG, "running post-transaction scripts\n");
+    runTransScripts(ts, PKG_POSTTRANS);
+    runTransScripts(ts, PKG_POSTUNTRANS);
+    /* run %transfiletriggerin scripts */
+    runFileTriggers(ts, NULL, -1, RPMSENSE_TRIGGERIN, RPMSCRIPT_TRANSFILETRIGGER, 0);
+    /* run %transfiletriggerpostun scrips */
+    runPostUnTransFileTrigs(ts);
+    /* Run immed %transfiletriggerin scripts */
+    runTransScripts(ts, PKG_TRANSFILETRIGGERIN);
+    rc = 0;
+
+exit:
+    (void) umask(oldmask);
+    rpmtxnEnd(txn);
+    sigaction(SIGPIPE, &oact, NULL);
+    rpmtsEmpty(ts);
+    return rc;
+}
--- tools/rpm.c.orig	2025-02-19 15:29:33.000000000 +0000
+++ tools/rpm.c	2025-04-14 13:20:00.056365254 +0000
@@ -21,6 +21,7 @@ enum modes {
     MODE_ERASE		= (1 <<  2),
     MODE_RESTORE	= (1 <<  4),
 #define	MODES_IE (MODE_INSTALL | MODE_ERASE)
+    MODE_RUNPOSTTRANS	= (1 <<  5),
 
     MODE_UNKNOWN	= 0
 };
@@ -115,6 +116,11 @@ int main(int argc, char *argv[])
 	int eflags = (ia->installInterfaceFlags & INSTALL_ERASE);
 	int rflags = (ia->installInterfaceFlags & INSTALL_RESTORE);
 
+	if (ia->installInterfaceFlags & INSTALL_RUNPOSTTRANS) {
+	    if (iflags || eflags || rflags)
+		argerror(_("only one major mode may be specified"));
+	    bigMode = MODE_RUNPOSTTRANS;
+	}
 	if (iflags & eflags & rflags)
 	    argerror(_("only one major mode may be specified"));
 	else if (iflags)
@@ -293,6 +299,14 @@ int main(int argc, char *argv[])
 	ec = rpmcliVerify(ts, qva, (ARGV_const_t) poptGetArgs(optCon));
     }	break;
 
+    case MODE_RUNPOSTTRANS:
+	if (!poptPeekArg(optCon)) {
+	    argerror(_("need posttrans manifest for --runposttrans"));
+	} else {
+	    ec += rpmRunPostTrans(ts, ia, (ARGV_const_t) poptGetArgs(optCon));
+	}
+	break;
+
     case MODE_UNKNOWN:
 	if (poptPeekArg(optCon) != NULL || argc <= 1) {
 	    printUsage(optCon, stderr, 0);
