When I started thinking about building a phishing investigation application in Tines 3B, the obvious place to begin would have been the phishing workflow itself.
Take in a suspicious email, extract the indicators, enrich them, score what comes back, summarize the findings, and present everything to the analyst.
That would work.
It would also be the wrong place for me to start.
The phishing application is going to need threat intelligence, but threat intelligence is not specific to phishing. The same lookup logic is going to show up in other security workflows I build later. I may want to investigate an IP address from an alert, enrich a domain from a DNS event, inspect a suspicious URL from a proxy log, or check an email address as part of another investigation.
If I embed all of that logic directly into the phishing application, I end up solving the same problem repeatedly.
So instead of starting with the application, I started with the capability.
The first thing I built was a reusable threat intelligence workflow that other workflows can call.
That sounds like a small architectural decision, but I think it becomes increasingly important as AI-assisted application building gets faster. Tools like 3B make it very easy to describe a large application and let the system start producing it. The danger is that “easy to build” can quickly turn into “easy to build too much in one place.”
My goal with this project is not simply to see how much I can ask 3B to generate in one prompt. I want to see how I can use it to build something I would actually want to maintain.
Thinking About the End State First
Before I gave 3B anything to build, I thought through what the final phishing investigation application was going to need.
At a minimum, it would need to understand the indicators extracted from a suspicious email and enrich them against multiple sources. An IP address may need a different set of lookups than a domain. A URL might go to another provider. An email address may need its own reputation source.
For the first version, I wanted to work with AbuseIPDB, EmailRep, urlscan.io, and VirusTotal.
Here is the Prompt:
Those services all provide useful information, but they are independent APIs with different schemas, authentication methods, response structures, error conditions, and data models.
If I were writing a one-off workflow, I could simply add four API calls and deal with each response wherever I needed it.
But that immediately creates a coupling problem.
The phishing application now has to know how VirusTotal works.
It has to know how AbuseIPDB works.
It has to know what EmailRep returns.
It has to know what urlscan.io expects.
Then the next application I build has to know the same things all over again.
A better model is to give the application one thing to understand: the threat intelligence capability.
The application provides an indicator. The threat intelligence workflow figures out what kind of indicator it received, calls the appropriate sources, collects the results, and returns a consistent response.
That leaves the calling application focused on the investigation rather than the mechanics of every enrichment provider.
Telling 3B That Reuse Is a Requirement
One of the more important parts of the prompt I gave 3B was that this workflow needed to be reusable by other applications.
I did not just ask it to check an indicator against a few APIs.
I told it that the workflow was going to become a dependency for other workflows.
That changes the problem.
As soon as another application is expected to consume what you are building, you need to think about interfaces.
What does the workflow accept?
What does it return?
How are errors represented?
How does the caller know whether an indicator was an IP address, URL, domain, or email address?
What happens if one provider succeeds and another fails?
Do the raw provider results remain available?
Does the workflow return a normalized structure?
Those are software design questions more than automation questions.
That is one of the things I find interesting about 3B. You can start with a fairly high-level description of what you want, but if you give it architectural constraints, those constraints influence what gets built underneath.
In this case, 3B began building the threat intelligence capability as an executable step rather than just assembling a collection of visual actions.
What 3B Is Actually Building
As the workflow was being generated, I spent some time looking at what 3B was creating behind the scenes.
The first version used TypeScript.
That was not a hard requirement. If Python had made more sense for the problem, I could have asked it to build the backend logic in Python instead.
What mattered more to me was that the generated component was not locked into some special proprietary language that only existed inside the builder.
There was actual code.
There was configuration.
There was a config.toml.
There was a Dockerfile.
There was documentation explaining how the step worked and how to call it.
When the step runs, the code executes in an isolated container.
That part is worth calling out because there is an important difference between using AI to generate an application and treating the generated application as a black box.
I am completely comfortable using AI to accelerate the build.
What I do not want is a system where I can no longer tell what is actually going to happen when the workflow executes.
With this approach, I can inspect what 3B created.
I can read the code.
I can change it.
I can inspect the dependencies.
I can review the configuration.
I can test the output.
If something does not work the way I expect, I have something concrete to debug.
The AI is helping with the construction of the capability, but the resulting implementation is still something I can reason about.
For me, that is a much more useful model for AI-assisted automation.
Separating Enrichment From Interpretation
Another design decision I care about is keeping the threat intelligence data separate from the AI interpretation of that data.
The enrichment providers are the sources of evidence.
If VirusTotal reports something about a domain, that result matters.
If AbuseIPDB returns reputation information for an IP address, that matters.
If urlscan.io has observed a URL in previous scans, that result matters.
The AI summary is useful, but it should not replace those underlying results.
So the model I want is straightforward.
The workflow accepts an indicator.
It determines what type of indicator it is.
It calls the sources that make sense for that indicator.
It collects and normalizes the responses.
Then AI can help summarize those results into something that is easier for an analyst or another workflow to consume.
That gives me both layers.
I have the structured data when I need to inspect the actual evidence, and I have a summarized interpretation when I want to quickly understand what the providers collectively found.
This becomes especially important later when the phishing investigation application starts making decisions based on enrichment.
I do not want the rest of the application depending only on a paragraph produced by a model. I want the underlying data available throughout the process.
The Interface Becomes the Important Part
Once I started thinking about this as a reusable capability, the API contract became more important than any individual integration.
The rest of the application should not care whether the indicator was checked against one provider or four.
It should not need to know whether VirusTotal changed one of its response fields.
It should not need to know which authentication method AbuseIPDB uses.
The calling application should be able to say, effectively:
Here is an indicator. Tell me what you know about it.
The threat intelligence capability owns everything behind that request.
This also gives me a clean place to make improvements later.
Maybe I decide to add another threat intelligence source.
Maybe I want to change the scoring logic.
Maybe one provider becomes unavailable and I replace it with another.
Maybe I want to improve how indicators are classified.
If the interface remains stable, the phishing application does not need to change just because the implementation of threat intelligence changed.
That is a basic software architecture principle, but it applies just as much to automation workflows.
In fact, I think it matters even more when AI is helping us build because the cost of adding more logic is becoming so low.
AI Makes It Easier to Build a Monolith
Before tools like 3B, there was a natural amount of friction in building a large workflow.
Every integration took time.
Every branch took time.
Every transformation took time.
That friction was annoying, but it sometimes forced you to stop and think about whether something belonged in the same workflow.
With AI-assisted building, you can describe a much larger application and have the system start assembling it almost immediately.
That is incredibly useful, but it also makes it easier to accidentally build a monolith.
You start with phishing investigation.
Then you add threat intelligence.
Then reputation scoring.
Then URL analysis.
Then email enrichment.
Then summarization.
Then reporting.
Then maybe case creation.
Then analyst notifications.
Pretty soon you have one giant application that knows about everything.
It may work.
It may even have been fast to create.
But speed of construction and quality of architecture are not the same thing.
The question I am trying to ask throughout this series is not simply, “Can 3B build this?”
It is, “How should I structure this so I still want to work on it six months from now?”
That is why I started with threat intelligence as its own capability.
Building Once Changes the Next Build
Once 3B finished creating the workflow, I could review the README, inspect the expected input and output, look at the code and configuration, and understand how the capability was meant to be consumed.
At that point, I could publish it and treat it as a building block.
Now when I move on to the phishing application, I do not need to spend the first part of the build wiring up VirusTotal, AbuseIPDB, EmailRep, and urlscan.io again.
The phishing application can simply consume the threat intelligence capability.
That lets me move up one level of abstraction.
Instead of asking:
“How do I call these four APIs?”
I can ask:
“What should my phishing investigation do with the enrichment results?”
That is a much more interesting problem.
And the same applies to the next security workflow I build.
If it needs threat intelligence, the capability is already there.
Build Capabilities, Then Compose Applications
The biggest takeaway from this first part of the project has less to do with threat intelligence than it does with how I want to approach building with 3B.
I do not want every prompt to produce one giant application.
I want to identify reusable capabilities and build those as independent pieces where it makes sense.
Then I can compose larger applications from those pieces.
Threat intelligence is the first example.
The phishing investigation application will be the first consumer.
Later, other workflows can use the same capability.
And if I improve that capability, those consumers can benefit without each one having to reinvent the enrichment logic.
That is the architecture I want as these applications get larger.
Build the capability once.
Give it a clean interface.
Understand what was created.
Then reuse it everywhere it makes sense.
In the next part of the series, I will start building the phishing investigation application itself. This time, instead of recreating all of the threat intelligence logic inside it, I will plug in the workflow I already built and start focusing on the actual investigation process.



