[{"data":1,"prerenderedAt":1485},["ShallowReactive",2],{"page-\u002Fworkflows\u002Fsteps":3,"all-pages":1163},{"id":4,"title":5,"body":6,"description":1154,"extension":1155,"meta":1156,"navigation":312,"path":1157,"robots":1158,"seo":1159,"sitemap":1160,"stem":1161,"__hash__":1162},"content\u002F01.workflows\u002F03.steps.md","Steps",{"type":7,"value":8,"toc":1132},"minimark",[9,22,90,95,98,196,209,262,272,284,353,398,410,416,429,511,518,537,543,552,599,604,613,674,712,722,726,739,769,774,777,828,832,835,842,913,942,949,1006,1014,1021,1050,1071,1081,1123,1128],[10,11,12,13,17,18,21],"p",{},"Steps are the units of execution inside an activity. This page covers the two\nexecution primitives (",[14,15,16],"code",{},"step.script"," and ",[14,19,20],{},"step.exec",") and artifact plumbing.\nOther step kinds are covered in their own pages:",[23,24,25,45,54,63,72,81],"ul",{},[26,27,28,33,34,37,38,41,42],"li",{},[29,30,32],"a",{"href":31},"\u002Fworkflows\u002Fconcurrency","Concurrency"," — ",[14,35,36],{},"step.parallel",", ",[14,39,40],{},"step.spawn",",\n",[14,43,44],{},"step.join",[26,46,47,33,51],{},[29,48,50],{"href":49},"\u002Fworkflows\u002Ffor-each","For Each",[14,52,53],{},"step.for_each",[26,55,56,33,60],{},[29,57,59],{"href":58},"\u002Fworkflows\u002Ftransforms","Transforms",[14,61,62],{},"step.transform",[26,64,65,33,69],{},[29,66,68],{"href":67},"\u002Fworkflows\u002Fmodules","Modules",[14,70,71],{},"step.module",[26,73,74,33,78],{},[29,75,77],{"href":76},"\u002Fworkflows\u002Fagents","Agents",[14,79,80],{},"step.agent",[26,82,83,33,87],{},[29,84,86],{"href":85},"\u002Fdatasets\u002Fworkflows","Datasets",[14,88,89],{},"step.update_dataset",[91,92,94],"h2",{"id":93},"common-step-parameters","Common Step Parameters",[10,96,97],{},"Most steps accept:",[99,100,101,117],"table",{},[102,103,104],"thead",{},[105,106,107,111,114],"tr",{},[108,109,110],"th",{},"Parameter",[108,112,113],{},"Default",[108,115,116],{},"Meaning",[118,119,120,142,154,169,184],"tbody",{},[105,121,122,128,131],{},[123,124,125],"td",{},[14,126,127],{},"id",[123,129,130],{},"—",[123,132,133,134,137,138,141],{},"Stable key for ",[14,135,136],{},"run.steps.\u003Cid>"," lookups and ",[14,139,140],{},"scan.succeeded()"," predicates",[105,143,144,149,151],{},[123,145,146],{},[14,147,148],{},"name",[123,150,130],{},[123,152,153],{},"Human-facing label shown in the UI (defaults are derived)",[105,155,156,161,166],{},[123,157,158],{},[14,159,160],{},"cond",[123,162,163],{},[14,164,165],{},"run.ok",[123,167,168],{},"Run the step only when this runtime predicate holds",[105,170,171,176,181],{},[123,172,173],{},[14,174,175],{},"allow_failure",[123,177,178],{},[14,179,180],{},"False",[123,182,183],{},"Do not fail the activity if the step fails",[105,185,186,191,193],{},[123,187,188],{},[14,189,190],{},"timeout",[123,192,130],{},[123,194,195],{},"Bound the step's execution time",[10,197,198,200,201,17,203,205,206,208],{},[14,199,40],{}," is the exception: it returns a branch handle, so it accepts\n",[14,202,148],{},[14,204,190],{}," only (its ",[14,207,175],{}," parameter is accepted but\nignored).",[210,211,216],"pre",{"className":212,"code":213,"language":214,"meta":215,"style":215},"language-bhw shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","step.exec(\n    command = [\"ffuf\", \"-w\", \"wordlist.txt\", \"-u\", target, \"-o\", \"ffuf.json\"],\n    id = \"ffuf-dirs\",\n    timeout = duration.minutes(30),\n    # Directory brute-forcing is noisy; keep going even if it exits non-zero.\n    allow_failure = True,\n)\n","bhw","",[14,217,218,226,232,238,244,250,256],{"__ignoreMap":215},[219,220,223],"span",{"class":221,"line":222},"line",1,[219,224,225],{},"step.exec(\n",[219,227,229],{"class":221,"line":228},2,[219,230,231],{},"    command = [\"ffuf\", \"-w\", \"wordlist.txt\", \"-u\", target, \"-o\", \"ffuf.json\"],\n",[219,233,235],{"class":221,"line":234},3,[219,236,237],{},"    id = \"ffuf-dirs\",\n",[219,239,241],{"class":221,"line":240},4,[219,242,243],{},"    timeout = duration.minutes(30),\n",[219,245,247],{"class":221,"line":246},5,[219,248,249],{},"    # Directory brute-forcing is noisy; keep going even if it exits non-zero.\n",[219,251,253],{"class":221,"line":252},6,[219,254,255],{},"    allow_failure = True,\n",[219,257,259],{"class":221,"line":258},7,[219,260,261],{},")\n",[91,263,265,266,37,268,271],{"id":264},"conditions-cond-when-and-step-predicates","Conditions: ",[14,267,160],{},[14,269,270],{},".when(...)"," and step predicates",[10,273,274,275,279,280,283],{},"A condition is a ",[276,277,278],"strong",{},"runtime predicate"," ",[14,281,282],{},"F(run) -> bool",". Write it as a lambda\nor pass a step predicate. It is evaluated by the runner just before the step\nexecutes, never while the workflow is being defined:",[210,285,287],{"className":212,"code":286,"language":214,"meta":215,"style":215},"scan = step.exec(\n    command = [\"nuclei\", \"-l\", \"live.txt\"],\n    id = \"scan\",\n)\n\n# Same condition, three spellings:\nrecheck = step.exec(command = [\"httpx\", \"-l\", \"live.txt\"], cond = scan.succeeded())\nrecheck = step.exec(command = [\"httpx\", \"-l\", \"live.txt\"]).when(scan.succeeded())\nrecheck = step.exec(\n    command = [\"httpx\", \"-l\", \"live.txt\"],\n    cond = lambda run: run.steps.scan.conclusion == steps.Conclusion.SUCCEEDED,\n)\n",[14,288,289,294,299,304,308,314,319,324,330,336,342,348],{"__ignoreMap":215},[219,290,291],{"class":221,"line":222},[219,292,293],{},"scan = step.exec(\n",[219,295,296],{"class":221,"line":228},[219,297,298],{},"    command = [\"nuclei\", \"-l\", \"live.txt\"],\n",[219,300,301],{"class":221,"line":234},[219,302,303],{},"    id = \"scan\",\n",[219,305,306],{"class":221,"line":240},[219,307,261],{},[219,309,310],{"class":221,"line":246},[219,311,313],{"emptyLinePlaceholder":312},true,"\n",[219,315,316],{"class":221,"line":252},[219,317,318],{},"# Same condition, three spellings:\n",[219,320,321],{"class":221,"line":258},[219,322,323],{},"recheck = step.exec(command = [\"httpx\", \"-l\", \"live.txt\"], cond = scan.succeeded())\n",[219,325,327],{"class":221,"line":326},8,[219,328,329],{},"recheck = step.exec(command = [\"httpx\", \"-l\", \"live.txt\"]).when(scan.succeeded())\n",[219,331,333],{"class":221,"line":332},9,[219,334,335],{},"recheck = step.exec(\n",[219,337,339],{"class":221,"line":338},10,[219,340,341],{},"    command = [\"httpx\", \"-l\", \"live.txt\"],\n",[219,343,345],{"class":221,"line":344},11,[219,346,347],{},"    cond = lambda run: run.steps.scan.conclusion == steps.Conclusion.SUCCEEDED,\n",[219,349,351],{"class":221,"line":350},12,[219,352,261],{},[23,354,355,371,387],{},[26,356,357,359,360,363,364,367,368,370],{},[14,358,140],{}," \u002F ",[14,361,362],{},"scan.failed()"," return a predicate ",[14,365,366],{},"run -> bool",";\nthey require the referenced step to have an ",[14,369,127],{},".",[26,372,373,17,376,379,380,37,383,386],{},[14,374,375],{},".when(F)",[14,377,378],{},"cond = F"," are the same mechanism. Static booleans are\nrejected (",[14,381,382],{},"cond = True",[14,384,385],{},".when(True)",") so a condition can never be\nevaluated too early.",[26,388,389,390,393,394,397],{},"A step predicate can only test an earlier step in the ",[276,391,392],{},"same activity",".\nCross-activity wiring is ",[14,395,396],{},"trigger.on(scan.succeeded())"," — that is an\nactivity event, not a step condition.",[10,399,400,401,404,405,409],{},"Steps can be bound to variables so later conditions can inspect them via\n",[14,402,403],{},"run.steps"," (see the ",[29,406,408],{"href":407},"\u002Fworkflows\u002Fsyntax-reference#conditions","Syntax Reference",").",[91,411,413,414],{"id":412},"shell-scripts-stepscript","Shell Scripts: ",[14,415,16],{},[10,417,418,420,421,424,425,428],{},[14,419,16],{}," is the ",[276,422,423],{},"explicit"," shell API. It runs a script under a shell\n(",[14,426,427],{},"bash"," by default):",[210,430,432],{"className":212,"code":431,"language":214,"meta":215,"style":215},"activity(\n    name = \"notify\",\n    triggers = [trigger.on(scan.succeeded())],\n    steps = [\n        step.script(content = \"\"\"\n            echo \"New findings on ${TARGET}:\" >&2\n            cat findings.txt\n        \"\"\"),\n        step.script(\n            content = \"curl -s https:\u002F\u002Fapi.example.com\u002Fping\",\n            shell = \"bash\",\n            allow_failure = True,\n        ),\n    ],\n)\n",[14,433,434,439,444,449,454,459,464,469,474,479,484,489,494,500,506],{"__ignoreMap":215},[219,435,436],{"class":221,"line":222},[219,437,438],{},"activity(\n",[219,440,441],{"class":221,"line":228},[219,442,443],{},"    name = \"notify\",\n",[219,445,446],{"class":221,"line":234},[219,447,448],{},"    triggers = [trigger.on(scan.succeeded())],\n",[219,450,451],{"class":221,"line":240},[219,452,453],{},"    steps = [\n",[219,455,456],{"class":221,"line":246},[219,457,458],{},"        step.script(content = \"\"\"\n",[219,460,461],{"class":221,"line":252},[219,462,463],{},"            echo \"New findings on ${TARGET}:\" >&2\n",[219,465,466],{"class":221,"line":258},[219,467,468],{},"            cat findings.txt\n",[219,470,471],{"class":221,"line":326},[219,472,473],{},"        \"\"\"),\n",[219,475,476],{"class":221,"line":332},[219,477,478],{},"        step.script(\n",[219,480,481],{"class":221,"line":338},[219,482,483],{},"            content = \"curl -s https:\u002F\u002Fapi.example.com\u002Fping\",\n",[219,485,486],{"class":221,"line":344},[219,487,488],{},"            shell = \"bash\",\n",[219,490,491],{"class":221,"line":350},[219,492,493],{},"            allow_failure = True,\n",[219,495,497],{"class":221,"line":496},13,[219,498,499],{},"        ),\n",[219,501,503],{"class":221,"line":502},14,[219,504,505],{},"    ],\n",[219,507,509],{"class":221,"line":508},15,[219,510,261],{},[10,512,513,514,517],{},"Script content can also be a ",[276,515,516],{},"function",", which is evaluated at execution time\nwith the run context — useful for templating from dispatch inputs or earlier\nresults:",[210,519,521],{"className":212,"code":520,"language":214,"meta":215,"style":215},"step.script(\n    content = lambda run: \"subfinder -d {} -o subs.txt\".format(run.inputs[\"domain\"]),\n)\n",[14,522,523,528,533],{"__ignoreMap":215},[219,524,525],{"class":221,"line":222},[219,526,527],{},"step.script(\n",[219,529,530],{"class":221,"line":228},[219,531,532],{},"    content = lambda run: \"subfinder -d {} -o subs.txt\".format(run.inputs[\"domain\"]),\n",[219,534,535],{"class":221,"line":234},[219,536,261],{},[91,538,540,541],{"id":539},"process-execution-stepexec","Process Execution: ",[14,542,20],{},[10,544,545,547,548,551],{},[14,546,20],{}," runs a program directly from a ",[276,549,550],{},"complete argv vector",". There is no\nimplicit shell: element 0 is the program, and the process receives exactly what\nyou declare.",[210,553,555],{"className":212,"code":554,"language":214,"meta":215,"style":215},"step.exec(\n    command = [\n        \"nuclei\",\n        \"-l\", \"live.txt\",\n        \"-severity\", config.var(\"nuclei_severity\"),\n        \"-o\", \"findings.txt\",\n    ],\n    env = {\"NUCLEI_TECHNOLOGIES\": \"wordpress\"},\n)\n",[14,556,557,561,566,571,576,581,586,590,595],{"__ignoreMap":215},[219,558,559],{"class":221,"line":222},[219,560,225],{},[219,562,563],{"class":221,"line":228},[219,564,565],{},"    command = [\n",[219,567,568],{"class":221,"line":234},[219,569,570],{},"        \"nuclei\",\n",[219,572,573],{"class":221,"line":240},[219,574,575],{},"        \"-l\", \"live.txt\",\n",[219,577,578],{"class":221,"line":246},[219,579,580],{},"        \"-severity\", config.var(\"nuclei_severity\"),\n",[219,582,583],{"class":221,"line":252},[219,584,585],{},"        \"-o\", \"findings.txt\",\n",[219,587,588],{"class":221,"line":258},[219,589,505],{},[219,591,592],{"class":221,"line":326},[219,593,594],{},"    env = {\"NUCLEI_TECHNOLOGIES\": \"wordpress\"},\n",[219,596,597],{"class":221,"line":332},[219,598,261],{},[600,601,603],"h3",{"id":602},"explicit-everything","Explicit everything",[10,605,606,608,609,612],{},[14,607,20],{}," has ",[276,610,611],{},"no implicit workflow data",". The process receives exactly the\nexplicitly provided command, environment, stdin, and working directory:",[23,614,615,640,646,665],{},[26,616,617,620,621,627,628,631,632,635,636,639],{},[14,618,619],{},"command"," — the complete argv vector. Elements may be literal strings,\nreferences to files produced by earlier steps, ",[29,622,624],{"href":623},"\u002Fworkflows\u002Fconfig",[14,625,626],{},"config.*","\nvalues, module string inputs, or lazy runtime expressions. Module file and\ndirectory inputs cannot be used as raw command elements; format them into\nargv (for example ",[14,629,630],{},"\"{}\".format(targets)",") instead. A nested list is an\n",[276,633,634],{},"optional argv group",": it expands only when every optional module input it\nreferences is bound, otherwise the whole group is dropped. A bare optional\nmodule input element is dropped when unbound — so CLI flags vanish instead\nof being passed empty. Concatenating a string literal with a bool module\ninput using ",[14,637,638],{},"+"," is rejected; use a conditional instead.",[26,641,642,645],{},[14,643,644],{},"env"," — only the entries you list. Values may be config references;\nsecret values are resolved at execution time and never serialized.",[26,647,648,359,651,359,654,657,658,661,662,664],{},[14,649,650],{},"stdin",[14,652,653],{},"stdout",[14,655,656],{},"stderr"," — optional; each must be a ",[14,659,660],{},"path.file(...)",".\nWithout an explicit ",[14,663,653],{},", process output is captured for logs but never\nbecomes workflow data.",[26,666,667,670,671,370],{},[14,668,669],{},"cwd"," — optional; must be a ",[14,672,673],{},"path.directory(...)",[210,675,677],{"className":212,"code":676,"language":214,"meta":215,"style":215},"step.exec(\n    command = [\"httpx\", \"-silent\", \"-status-code\", \"-o\", \"\u002Fdev\u002Fnull\"],\n    stdin = path.file(\"subs-clean.txt\"),      # feed targets on stdin\n    stdout = path.file(\"live.txt\"),           # persist stdout as a resource\n    stderr = path.file(\"httpx-errors.log\"),\n    env = {\"GITHUB_TOKEN\": config.secret(\"github_token\")},\n)\n",[14,678,679,683,688,693,698,703,708],{"__ignoreMap":215},[219,680,681],{"class":221,"line":222},[219,682,225],{},[219,684,685],{"class":221,"line":228},[219,686,687],{},"    command = [\"httpx\", \"-silent\", \"-status-code\", \"-o\", \"\u002Fdev\u002Fnull\"],\n",[219,689,690],{"class":221,"line":234},[219,691,692],{},"    stdin = path.file(\"subs-clean.txt\"),      # feed targets on stdin\n",[219,694,695],{"class":221,"line":240},[219,696,697],{},"    stdout = path.file(\"live.txt\"),           # persist stdout as a resource\n",[219,699,700],{"class":221,"line":246},[219,701,702],{},"    stderr = path.file(\"httpx-errors.log\"),\n",[219,704,705],{"class":221,"line":252},[219,706,707],{},"    env = {\"GITHUB_TOKEN\": config.secret(\"github_token\")},\n",[219,709,710],{"class":221,"line":258},[219,711,261],{},[10,713,714,715,718,719,721],{},"A non-zero exit code fails the step (unless ",[14,716,717],{},"allow_failure = True","). Timeouts\nare configured per step with ",[14,720,190],{},"; there is no per-step retry policy.",[600,723,725],{"id":724},"data-flow-defines-ordering","Data flow defines ordering",[10,727,728,729,37,731,733,734,738],{},"There is no implicit sequencing between independent processes beyond the order\nyou write them in. When a step's ",[14,730,619],{},[14,732,650],{},", or environment references a\nresource produced by an earlier step, that reference is both the data flow ",[735,736,737],"em",{},"and","\nthe scheduling order — the runner waits for the producer first:",[210,740,742],{"className":212,"code":741,"language":214,"meta":215,"style":215},"steps = [\n    step.exec(command = [\"subfinder\", \"-d\", domain, \"-o\", \"subs.txt\"], id = \"enum\"),\n    step.exec(command = [\"httpx\", \"-l\", \"subs.txt\", \"-o\", \"live.txt\"], id = \"probe\"),\n    step.exec(command = [\"nuclei\", \"-l\", \"live.txt\", \"-severity\", \"critical\", \"-o\", \"crit.txt\"], id = \"scan\"),\n]\n",[14,743,744,749,754,759,764],{"__ignoreMap":215},[219,745,746],{"class":221,"line":222},[219,747,748],{},"steps = [\n",[219,750,751],{"class":221,"line":228},[219,752,753],{},"    step.exec(command = [\"subfinder\", \"-d\", domain, \"-o\", \"subs.txt\"], id = \"enum\"),\n",[219,755,756],{"class":221,"line":234},[219,757,758],{},"    step.exec(command = [\"httpx\", \"-l\", \"subs.txt\", \"-o\", \"live.txt\"], id = \"probe\"),\n",[219,760,761],{"class":221,"line":240},[219,762,763],{},"    step.exec(command = [\"nuclei\", \"-l\", \"live.txt\", \"-severity\", \"critical\", \"-o\", \"crit.txt\"], id = \"scan\"),\n",[219,765,766],{"class":221,"line":246},[219,767,768],{},"]\n",[10,770,771,772,409],{},"Each step consumes the previous step's output file, so they execute strictly in\norder. Steps whose inputs are unrelated to each other should be declared as\nparallel branches instead (see ",[29,773,32],{"href":31},[10,775,776],{},"Optional inputs make reusable commands clean. Inside a module, an unbound\noptional input (and any nested list group containing only unbound optionals)\nis omitted from argv rather than passed as an empty string:",[210,778,780],{"className":212,"code":779,"language":214,"meta":215,"style":215},"# Module with an optional extra-flags input; callers may omit it.\nextra = input.string(name = \"extra_flags\", required = False, description = \"Optional extra flags\")\n\nmodule(\n    inputs = [extra],\n    outputs = [],\n    steps = [\n        step.exec(command = [\"nuclei\", \"-l\", \"live.txt\", [extra]]),\n    ],\n)\n",[14,781,782,787,792,796,801,806,811,815,820,824],{"__ignoreMap":215},[219,783,784],{"class":221,"line":222},[219,785,786],{},"# Module with an optional extra-flags input; callers may omit it.\n",[219,788,789],{"class":221,"line":228},[219,790,791],{},"extra = input.string(name = \"extra_flags\", required = False, description = \"Optional extra flags\")\n",[219,793,794],{"class":221,"line":234},[219,795,313],{"emptyLinePlaceholder":312},[219,797,798],{"class":221,"line":240},[219,799,800],{},"module(\n",[219,802,803],{"class":221,"line":246},[219,804,805],{},"    inputs = [extra],\n",[219,807,808],{"class":221,"line":252},[219,809,810],{},"    outputs = [],\n",[219,812,813],{"class":221,"line":258},[219,814,453],{},[219,816,817],{"class":221,"line":326},[219,818,819],{},"        step.exec(command = [\"nuclei\", \"-l\", \"live.txt\", [extra]]),\n",[219,821,822],{"class":221,"line":332},[219,823,505],{},[219,825,826],{"class":221,"line":338},[219,827,261],{},[91,829,831],{"id":830},"artifacts","Artifacts",[10,833,834],{},"Activities are isolated: files produced by one activity are not automatically\nvisible to the next. Artifacts are the way to hand files between activities.",[600,836,838,839],{"id":837},"uploading-stepupload_artifact","Uploading: ",[14,840,841],{},"step.upload_artifact",[210,843,845],{"className":212,"code":844,"language":214,"meta":215,"style":215},"discover = activity(\n    name = \"discover\",\n    triggers = [trigger.dispatch()],\n    steps = [\n        step.exec(command = [\"subfinder\", \"-d\", config.var(\"target_domain\"), \"-o\", \"subs.txt\"]),\n        step.upload_artifact(\n            name = \"subdomains\",\n            expires_in = duration.days(7),\n            entries = [\n                artifact_entry(source = \"subs.txt\", destination = \"subs.txt\"),\n            ],\n        ),\n    ],\n)\n",[14,846,847,852,857,862,866,871,876,881,886,891,896,901,905,909],{"__ignoreMap":215},[219,848,849],{"class":221,"line":222},[219,850,851],{},"discover = activity(\n",[219,853,854],{"class":221,"line":228},[219,855,856],{},"    name = \"discover\",\n",[219,858,859],{"class":221,"line":234},[219,860,861],{},"    triggers = [trigger.dispatch()],\n",[219,863,864],{"class":221,"line":240},[219,865,453],{},[219,867,868],{"class":221,"line":246},[219,869,870],{},"        step.exec(command = [\"subfinder\", \"-d\", config.var(\"target_domain\"), \"-o\", \"subs.txt\"]),\n",[219,872,873],{"class":221,"line":252},[219,874,875],{},"        step.upload_artifact(\n",[219,877,878],{"class":221,"line":258},[219,879,880],{},"            name = \"subdomains\",\n",[219,882,883],{"class":221,"line":326},[219,884,885],{},"            expires_in = duration.days(7),\n",[219,887,888],{"class":221,"line":332},[219,889,890],{},"            entries = [\n",[219,892,893],{"class":221,"line":338},[219,894,895],{},"                artifact_entry(source = \"subs.txt\", destination = \"subs.txt\"),\n",[219,897,898],{"class":221,"line":344},[219,899,900],{},"            ],\n",[219,902,903],{"class":221,"line":350},[219,904,499],{},[219,906,907],{"class":221,"line":496},[219,908,505],{},[219,910,911],{"class":221,"line":502},[219,912,261],{},[23,914,915,923,936],{},[26,916,917,919,920,370],{},[14,918,148],{}," names the artifact so downstream activities can reference it via\n",[14,921,922],{},"discover.artifacts.subdomains",[26,924,925,928,929,932,933,409],{},[14,926,927],{},"entries"," map files from the activity workspace (",[14,930,931],{},"source",") into the artifact\nlayout (",[14,934,935],{},"destination",[26,937,938,941],{},[14,939,940],{},"expires_in"," controls retention — useful for noisy raw scan output you do not\nneed to keep long.",[600,943,945,946],{"id":944},"downloading-stepdownload_artifact","Downloading: ",[14,947,948],{},"step.download_artifact",[210,950,952],{"className":212,"code":951,"language":214,"meta":215,"style":215},"probe = activity(\n    name = \"probe\",\n    triggers = [trigger.on(discover.succeeded())],\n    steps = [\n        step.download_artifact(discover.artifacts.subdomains),\n        step.exec(command = [\"httpx\", \"-l\", \"subs.txt\", \"-o\", \"live.txt\"]),\n        step.upload_artifact(name = \"live-hosts\", entries = [\n            artifact_entry(source = \"live.txt\", destination = \"live.txt\"),\n        ]),\n    ],\n)\n",[14,953,954,959,964,969,973,978,983,988,993,998,1002],{"__ignoreMap":215},[219,955,956],{"class":221,"line":222},[219,957,958],{},"probe = activity(\n",[219,960,961],{"class":221,"line":228},[219,962,963],{},"    name = \"probe\",\n",[219,965,966],{"class":221,"line":234},[219,967,968],{},"    triggers = [trigger.on(discover.succeeded())],\n",[219,970,971],{"class":221,"line":240},[219,972,453],{},[219,974,975],{"class":221,"line":246},[219,976,977],{},"        step.download_artifact(discover.artifacts.subdomains),\n",[219,979,980],{"class":221,"line":252},[219,981,982],{},"        step.exec(command = [\"httpx\", \"-l\", \"subs.txt\", \"-o\", \"live.txt\"]),\n",[219,984,985],{"class":221,"line":258},[219,986,987],{},"        step.upload_artifact(name = \"live-hosts\", entries = [\n",[219,989,990],{"class":221,"line":326},[219,991,992],{},"            artifact_entry(source = \"live.txt\", destination = \"live.txt\"),\n",[219,994,995],{"class":221,"line":332},[219,996,997],{},"        ]),\n",[219,999,1000],{"class":221,"line":338},[219,1001,505],{},[219,1003,1004],{"class":221,"line":344},[219,1005,261],{},[23,1007,1008],{},[26,1009,1010,1013],{},[14,1011,1012],{},"step.download_artifact(artifact)"," pulls a previously uploaded artifact into\nthe current activity workspace. There is no separate blob-store import\nprimitive: shared wordlists, nuclei templates, or resolver lists travel\nthrough artifacts.",[600,1015,1017,1018],{"id":1016},"reading-a-dataset-stepdownload_dataset","Reading a dataset: ",[14,1019,1020],{},"step.download_dataset",[210,1022,1024],{"className":212,"code":1023,"language":214,"meta":215,"style":215},"step.download_dataset(\n    table = \"subdomains\",\n    destination = path.file(\"known.csv\"),\n    format = \"csv\",\n)\n",[14,1025,1026,1031,1036,1041,1046],{"__ignoreMap":215},[219,1027,1028],{"class":221,"line":222},[219,1029,1030],{},"step.download_dataset(\n",[219,1032,1033],{"class":221,"line":228},[219,1034,1035],{},"    table = \"subdomains\",\n",[219,1037,1038],{"class":221,"line":234},[219,1039,1040],{},"    destination = path.file(\"known.csv\"),\n",[219,1042,1043],{"class":221,"line":240},[219,1044,1045],{},"    format = \"csv\",\n",[219,1047,1048],{"class":221,"line":246},[219,1049,261],{},[23,1051,1052],{},[26,1053,1054,1056,1057,1060,1061,1064,1065,1068,1069,370],{},[14,1055,1020],{}," exports a project dataset's current logical state into\nthe activity workspace (",[14,1058,1059],{},"\"csv\""," by default, or ",[14,1062,1063],{},"\"jsonl\"","\u002F",[14,1066,1067],{},"\"json\"","). Use it to\ndiff new findings against knowledge from earlier runs. See\n",[29,1070,86],{"href":85},[91,1072,1074,1075,17,1078],{"id":1073},"choosing-between-script-and-exec","Choosing Between ",[14,1076,1077],{},"script",[14,1079,1080],{},"exec",[99,1082,1083,1097],{},[102,1084,1085],{},[105,1086,1087,1093],{},[108,1088,1089,1090,1092],{},"Use ",[14,1091,16],{}," when...",[108,1094,1089,1095,1092],{},[14,1096,20],{},[118,1098,1099,1107,1115],{},[105,1100,1101,1104],{},[123,1102,1103],{},"You want pipes, redirection, variable expansion",[123,1105,1106],{},"You want a single tool invocation",[105,1108,1109,1112],{},[123,1110,1111],{},"Gluing multiple commands together",[123,1113,1114],{},"Precision matters: exact argv, exact env",[105,1116,1117,1120],{},[123,1118,1119],{},"Prototyping a phase quickly",[123,1121,1122],{},"Building reusable, typed steps",[10,1124,1125,1126,370],{},"Both fail the activity on non-zero exit unless ",[14,1127,717],{},[1129,1130,1131],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":215,"searchDepth":246,"depth":240,"links":1133},[1134,1135,1137,1139,1144,1152],{"id":93,"depth":228,"text":94},{"id":264,"depth":228,"text":1136},"Conditions: cond, .when(...) and step predicates",{"id":412,"depth":228,"text":1138},"Shell Scripts: step.script",{"id":539,"depth":228,"text":1140,"children":1141},"Process Execution: step.exec",[1142,1143],{"id":602,"depth":234,"text":603},{"id":724,"depth":234,"text":725},{"id":830,"depth":228,"text":831,"children":1145},[1146,1148,1150],{"id":837,"depth":234,"text":1147},"Uploading: step.upload_artifact",{"id":944,"depth":234,"text":1149},"Downloading: step.download_artifact",{"id":1016,"depth":234,"text":1151},"Reading a dataset: step.download_dataset",{"id":1073,"depth":228,"text":1153},"Choosing Between script and exec","Running scripts and processes with step.script and step.exec, and moving files between activities with artifacts.","md",{},"\u002Fworkflows\u002Fsteps",null,{"title":5,"description":1154},{"loc":1157},"01.workflows\u002F03.steps","vKX0vQbTRUiAt3lG86f6LsiCh2xCJZ4pMRQUj4XEOaY",[1164,1213,1229,1248,1281,1297,1309,1320,1331,1342,1366,1385,1405,1416,1427],{"title":1165,"path":1166,"stem":1167,"children":1168},"Workflows","\u002Fworkflows","01.workflows",[1169,1171,1175,1179,1183,1184,1188,1190,1192,1194,1196,1198,1202,1205,1209],{"title":1165,"path":1166,"stem":1170},"01.workflows\u002Findex",{"title":1172,"path":1173,"stem":1174},"Workflow Quick Reference","\u002Fworkflows\u002Fquick-reference","01.workflows\u002F00.quick-reference",{"title":1176,"path":1177,"stem":1178},"Workflows Overview","\u002Fworkflows\u002Foverview","01.workflows\u002F01.overview",{"title":1180,"path":1181,"stem":1182},"Workflow Syntax Reference","\u002Fworkflows\u002Fsyntax-reference","01.workflows\u002F02.syntax-reference",{"title":5,"path":1157,"stem":1161},{"title":1185,"path":1186,"stem":1187},"Step Outputs","\u002Fworkflows\u002Fstep-outputs","01.workflows\u002F04.step-outputs",{"title":32,"path":31,"stem":1189},"01.workflows\u002F05.concurrency",{"title":59,"path":58,"stem":1191},"01.workflows\u002F06.transforms",{"title":50,"path":49,"stem":1193},"01.workflows\u002F07.for-each",{"title":68,"path":67,"stem":1195},"01.workflows\u002F08.modules",{"title":77,"path":76,"stem":1197},"01.workflows\u002F09.agents",{"title":1199,"path":1200,"stem":1201},"Decisions","\u002Fworkflows\u002Fdecisions","01.workflows\u002F10.decisions",{"title":1203,"path":623,"stem":1204},"Configuration References","01.workflows\u002F11.config",{"title":1206,"path":1207,"stem":1208},"Comparison With Other Automation Systems","\u002Fworkflows\u002Fcomparison","01.workflows\u002F12.comparison",{"title":1210,"path":1211,"stem":1212},"Generate with AI","\u002Fworkflows\u002Fai-generation","01.workflows\u002F13.ai-generation",{"title":1214,"path":1215,"stem":1216,"children":1217},"Projects","\u002Fprojects","02.projects",[1218,1221,1225],{"title":1219,"path":1215,"stem":1220},"Projects & Runs","02.projects\u002Findex",{"title":1222,"path":1223,"stem":1224},"Projects Overview","\u002Fprojects\u002Foverview","02.projects\u002F1.overview",{"title":1226,"path":1227,"stem":1228},"Runs & Artifacts","\u002Fprojects\u002Fruns-artifacts","02.projects\u002F2.runs-artifacts",{"title":1230,"path":1231,"stem":1232,"children":1233},"Runners","\u002Frunners","03.runners",[1234,1236,1240,1244],{"title":1230,"path":1231,"stem":1235},"03.runners\u002Findex",{"title":1237,"path":1238,"stem":1239},"Runner Overview","\u002Frunners\u002Foverview","03.runners\u002F1.overview",{"title":1241,"path":1242,"stem":1243},"Getting Started with Runners","\u002Frunners\u002Fgetting-started","03.runners\u002F2.getting-started",{"title":1245,"path":1246,"stem":1247},"Custom Runner Images","\u002Frunners\u002Fcustom-image","03.runners\u002F3.custom-image",{"title":86,"path":1249,"stem":1250,"children":1251},"\u002Fdatasets","04.datasets",[1252,1254,1258,1262,1265,1269,1273,1277],{"title":86,"path":1249,"stem":1253},"04.datasets\u002Findex",{"title":1255,"path":1256,"stem":1257},"Datasets Overview","\u002Fdatasets\u002Foverview","04.datasets\u002F1.overview",{"title":1259,"path":1260,"stem":1261},"Dataset Schemas and Identity","\u002Fdatasets\u002Fschema","04.datasets\u002F2.schema",{"title":1263,"path":85,"stem":1264},"Pushing and Reading Dataset Rows","04.datasets\u002F3.workflows",{"title":1266,"path":1267,"stem":1268},"Dataset SQL Reference","\u002Fdatasets\u002Fsql","04.datasets\u002F4.sql",{"title":1270,"path":1271,"stem":1272},"Dataset Structured Filters","\u002Fdatasets\u002Ffilters","04.datasets\u002F5.filters",{"title":1274,"path":1275,"stem":1276},"Dataset Saved Views","\u002Fdatasets\u002Fviews","04.datasets\u002F6.views",{"title":1278,"path":1279,"stem":1280},"Datasets API and CLI","\u002Fdatasets\u002Fapi-cli","04.datasets\u002F7.api-cli",{"title":1282,"path":1283,"stem":1284,"children":1285},"Agent","\u002Fagent","05.agent",[1286,1289,1293],{"title":1287,"path":1283,"stem":1288},"BH Agent","05.agent\u002Findex",{"title":1290,"path":1291,"stem":1292},"BH Agent Overview","\u002Fagent\u002Foverview","05.agent\u002F1.overview",{"title":1294,"path":1295,"stem":1296},"MCP servers","\u002Fagent\u002Fmcp-servers","05.agent\u002F2.mcp-servers",{"title":1298,"path":1299,"stem":1300,"children":1301},"Bhlast","\u002Fbhlast","06.bhlast",[1302,1305],{"title":1303,"path":1299,"stem":1304},"Bhlast Servers","06.bhlast\u002Findex",{"title":1306,"path":1307,"stem":1308},"Bhlast servers overview","\u002Fbhlast\u002Foverview","06.bhlast\u002F1.overview",{"title":1310,"path":1311,"stem":1312,"children":1313},"Notifications","\u002Fnotifications","07.notifications",[1314,1316],{"title":1310,"path":1311,"stem":1315},"07.notifications\u002Findex",{"title":1317,"path":1318,"stem":1319},"Notifications Overview","\u002Fnotifications\u002Foverview","07.notifications\u002F1.overview",{"title":1321,"path":1322,"stem":1323,"children":1324},"Blob Storage","\u002Fblob-storage","08.blob-storage",[1325,1327],{"title":1321,"path":1322,"stem":1326},"08.blob-storage\u002Findex",{"title":1328,"path":1329,"stem":1330},"Blob Storage Overview","\u002Fblob-storage\u002Foverview","08.blob-storage\u002F1.overview",{"title":1332,"path":1333,"stem":1334,"children":1335},"Payloads","\u002Fpayloads","09.payloads",[1336,1338],{"title":1332,"path":1333,"stem":1337},"09.payloads\u002Findex",{"title":1339,"path":1340,"stem":1341},"Payloads overview","\u002Fpayloads\u002Foverview","09.payloads\u002F1.overview",{"title":1343,"path":1344,"stem":1345,"children":1346},"Cve Watcher","\u002Fcve-watcher","10.cve-watcher",[1347,1350,1354,1358,1362],{"title":1348,"path":1344,"stem":1349},"CVE Watcher","10.cve-watcher\u002Findex",{"title":1351,"path":1352,"stem":1353},"CVE Watcher Overview","\u002Fcve-watcher\u002Foverview","10.cve-watcher\u002F1.overview",{"title":1355,"path":1356,"stem":1357},"CVE Watcher Search and Filters","\u002Fcve-watcher\u002Fsearch","10.cve-watcher\u002F2.search",{"title":1359,"path":1360,"stem":1361},"CVE Watcher Push Notifications","\u002Fcve-watcher\u002Fnotifications","10.cve-watcher\u002F3.notifications",{"title":1363,"path":1364,"stem":1365},"CVE Watcher API and Scopes","\u002Fcve-watcher\u002Fapi","10.cve-watcher\u002F4.api",{"title":1367,"path":1368,"stem":1369,"children":1370},"Developer","\u002Fdeveloper","11.developer",[1371,1373,1377,1381],{"title":1367,"path":1368,"stem":1372},"11.developer\u002Findex",{"title":1374,"path":1375,"stem":1376},"Developer Overview","\u002Fdeveloper\u002Foverview","11.developer\u002F1.overview",{"title":1378,"path":1379,"stem":1380},"Personal Access Token Scopes","\u002Fdeveloper\u002Fscopes","11.developer\u002F2.scopes",{"title":1382,"path":1383,"stem":1384},"Personal Access Tokens","\u002Fdeveloper\u002Fpersonal-access-tokens","11.developer\u002F3.personal-access-tokens",{"title":1386,"path":1387,"stem":1388,"children":1389},"Bh","\u002Fbh","12.bh",[1390,1393,1397,1401],{"title":1391,"path":1387,"stem":1392},"BH CLI","12.bh\u002Findex",{"title":1394,"path":1395,"stem":1396},"BH CLI Overview","\u002Fbh\u002Foverview","12.bh\u002F1.overview",{"title":1398,"path":1399,"stem":1400},"BH CLI Commands","\u002Fbh\u002Fcommands","12.bh\u002F2.commands",{"title":1402,"path":1403,"stem":1404},"BH CLI Examples","\u002Fbh\u002Fexamples","12.bh\u002F3.examples",{"title":1406,"path":1407,"stem":1408,"children":1409},"Billing","\u002Fbilling","13.billing",[1410,1412],{"title":1406,"path":1407,"stem":1411},"13.billing\u002Findex",{"title":1413,"path":1414,"stem":1415},"Billing Overview","\u002Fbilling\u002Foverview","13.billing\u002F1.overview",{"title":1417,"path":1418,"stem":1419,"children":1420},"Tools","\u002Ftools","14.tools",[1421,1423],{"title":1417,"path":1418,"stem":1422},"14.tools\u002Findex",{"title":1424,"path":1425,"stem":1426},"Cookie Lab","\u002Ftools\u002Fcookie-lab","14.tools\u002F1.cookie-lab",{"title":1428,"path":1429,"stem":1430,"children":1431},"Site Policies","\u002Fsite-policies","99.site-policies",[1432,1434,1447,1451,1455,1459,1463,1467,1471],{"title":1428,"path":1429,"stem":1433},"99.site-policies\u002Findex",{"title":1435,"path":1436,"stem":1437,"children":1438},"Privacy Policy","\u002Fsite-policies\u002Fprivacy-policy","99.site-policies\u002F1.privacy-policy",[1439,1443],{"title":1440,"path":1441,"stem":1442},"Company Processors","\u002Fsite-policies\u002Fprivacy-policy\u002Fcompany-processors","99.site-policies\u002Fprivacy-policy\u002F1.company-processors",{"title":1444,"path":1445,"stem":1446},"Data Export Instructions","\u002Fsite-policies\u002Fprivacy-policy\u002Fdata-export-instructions","99.site-policies\u002Fprivacy-policy\u002F2.data-export-instructions",{"title":1448,"path":1449,"stem":1450},"Terms of Service","\u002Fsite-policies\u002Fterms-of-service","99.site-policies\u002F2.terms-of-service",{"title":1452,"path":1453,"stem":1454},"California Resident Notice at Collection","\u002Fsite-policies\u002Fccpa","99.site-policies\u002F3.ccpa",{"title":1456,"path":1457,"stem":1458},"Cancellation Policy","\u002Fsite-policies\u002Fcancellation-policy","99.site-policies\u002F4.cancellation-policy",{"title":1460,"path":1461,"stem":1462},"Refund Policy","\u002Fsite-policies\u002Frefund-policy","99.site-policies\u002F5.refund-policy",{"title":1464,"path":1465,"stem":1466},"Taxes on Services","\u002Fsite-policies\u002Ftaxes-on-services","99.site-policies\u002F6.taxes-on-services",{"title":1468,"path":1469,"stem":1470},"Disclaimer","\u002Fsite-policies\u002Fdisclaimer","99.site-policies\u002F7.disclaimer",{"title":1472,"path":1473,"stem":1474,"children":1475},"Security","\u002Fsite-policies\u002Fsecurity","99.site-policies\u002Fsecurity\u002Findex",[1476,1477,1481],{"title":1472,"path":1473,"stem":1474},{"title":1478,"path":1479,"stem":1480},"Security Overview","\u002Fsite-policies\u002Fsecurity\u002Fsecurity-overview","99.site-policies\u002Fsecurity\u002F1.security-overview",{"title":1482,"path":1483,"stem":1484},"Security Response","\u002Fsite-policies\u002Fsecurity\u002Fsecurity-response","99.site-policies\u002Fsecurity\u002F2.security-response",1794410312375]