Instructions to use simpledirect/Vinci-Cyber-8B-1.0 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use simpledirect/Vinci-Cyber-8B-1.0 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="simpledirect/Vinci-Cyber-8B-1.0") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("simpledirect/Vinci-Cyber-8B-1.0") model = AutoModelForCausalLM.from_pretrained("simpledirect/Vinci-Cyber-8B-1.0", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use simpledirect/Vinci-Cyber-8B-1.0 with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "simpledirect/Vinci-Cyber-8B-1.0" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "simpledirect/Vinci-Cyber-8B-1.0", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/simpledirect/Vinci-Cyber-8B-1.0
- SGLang
How to use simpledirect/Vinci-Cyber-8B-1.0 with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "simpledirect/Vinci-Cyber-8B-1.0" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "simpledirect/Vinci-Cyber-8B-1.0", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "simpledirect/Vinci-Cyber-8B-1.0" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "simpledirect/Vinci-Cyber-8B-1.0", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use simpledirect/Vinci-Cyber-8B-1.0 with Docker Model Runner:
docker model run hf.co/simpledirect/Vinci-Cyber-8B-1.0
- Vinci-Cyber-8B-1.0
- What it does
- Model at a glance
- Should you use this model?
- Intended use
- Evaluation and limitations of the evidence
- General capability against the base model
- Training
- Merged weights and numerical fidelity
- Loading and deployment
- Safety and scope
- Provenance and integrity
- Licence and attribution
- Citation
Vinci-Cyber-8B-1.0
Canadian-developed. Open weights. Focused on defensive infrastructure.
Vinci-Cyber-8B-1.0 is an 8B-class security-focused adaptation of IBM Granite 4.1 8B, developed by SimpleDirect, a Canadian AI lab. Its training centres on three infrastructure-as-code behaviours: proposing repairs, leaving clean configurations unchanged, and recovering from unsuccessful attempts.
The release gives security engineers, infrastructure teams, and researchers full model weights to evaluate, self-host, and adapt under Apache-2.0. It is intended for human-reviewed defensive workflows—not unattended changes to production infrastructure.
Your infrastructure. Your review process. Open weights to build on. The model does not require a hosted Vinci API. SimpleDirect developed the specialization and release; IBM developed the underlying Granite model. This is a Canadian contribution built on an explicitly credited open-weight foundation, not a foundation model pretrained from scratch by SimpleDirect.
What it does
Give it a scanner finding and the configuration that triggered it; it proposes the minimal edit that clears the finding. The example below comes from a real run against this repository revision — the model was loaded from this repo and these are its own words, not an illustration we wrote. Two things are edited for readability and we would rather name them than let you find them: the model prefaced and followed its answer with a sentence of explanation, and only the configuration block is shown; and the prompt below omits the system message the model actually received, which was "You are a defensive security assistant. Propose a minimal fix for the reported finding, or state that no change is needed." That instruction matters — it is what licenses the "no change needed" behaviour shown further down.
Input
Finding: CKV_AWS_3 — "Ensure all data stored in the EBS volume is securely encrypted"
resource "aws_ebs_volume" "data" {
availability_zone = "us-east-1a"
size = 100
encrypted = false
}
Propose the minimal edit that clears the finding without removing the resource.
Output
resource "aws_ebs_volume" "data" {
availability_zone = "us-east-1a"
size = 100
encrypted = true
}
It flipped one attribute. It did not delete the resource, set count = 0, widen a policy, or
rewrite unrelated fields — the shortcuts that make a finding disappear without fixing anything. Those
shortcuts are what the no-change and recovery halves of the training corpus are meant to discourage;
one example cannot show that the training is why this run avoided them.
Three more, from the same model but a different harness
All three were generated against this repository revision with greedy decoding, but not under the same settings as the example above: they were run through a separate script that sends no system message and caps output at 320 tokens, against 512 above. That difference is visible in the third one, which stops mid-sentence because it hit the cap — that is a truncated generation, not an edit by us. Nothing is reworded.
🔴 We chose these scenarios, and you should read them knowing that. No run was discarded and no output was selected after seeing it — the script defines exactly these three and all three are printed. But the scenarios themselves are ours, and the worked example above is a case that already appears in our internal panel as one the model solved. None of these four is a measurement. The measurements are further down, and they are more mixed than these examples look.
Publicly readable object storage. Given an aws_s3_bucket_acl with acl = "public-read" and the
CKV_AWS_20 finding:
resource "aws_s3_bucket_acl" "logs" { bucket = aws_s3_bucket.logs.id acl = "private" # or "authenticated-read" if you need authenticated users to have read access }
One attribute changed. The bucket, its reference and everything around it are untouched, and it names the alternative rather than silently choosing for you.
A file that is already correct. Given a Postgres ingress rule scoped to a specific source security group, with no finding reported:
"No change is needed. The provided Terraform configuration ... already specifies the necessary details to allow inbound traffic on port 5432 ... from the
appsecurity group to thedbsecurity group using TCP protocol."
This is the behaviour most worth checking in a remediation model: one that always edits will eventually edit something that was right. Declining is the correct answer here. Two honest caveats — the prompt told the model no finding had been reported and explicitly offered "if no change is needed, say so", so this is a much easier case than silence would be; and we did not run the parent on it, so this shows the behaviour exists, not that training produced it.
Something outside its specialty. Asked to fix a SQL injection in a Python function — application code, not infrastructure — it correctly identified string concatenation as the flaw and produced a parameterized query:
def get_user(conn, uid):
query = "SELECT * FROM users WHERE id = ?"
return conn.execute(query, (uid,)).fetchone()
The fix is right. But it then kept going, adding variants for other database libraries well past the answer, until it hit the output cap mid-word. We show this deliberately, and we are careful about what it does and does not corroborate. Our out-of-domain evaluation found no demonstrated repair improvement over the parent outside infrastructure-as-code — that part it illustrates. It is not the over-generation our evaluation measured: on that benchmark this model answered more tersely than its parent, and the failure we counted was a different one, degenerate repetition of a single edit block instead of stopping, in 3 of 230 cases. Cap your output length regardless.
🔴 These are four demonstrations, not a measurement. They show intended behaviour on four cases we picked; they establish no success rate, and the rates we do have are discussed under Evaluation below. The script under Loading and deployment below lets you load this model and generate from it yourself — but be clear about what that does and does not show: it uses a different prompt and a third set of generation settings, so it confirms the model is real, downloadable and working. It does not reproduce the four outputs above. Doing that needs the same prompt, the same system message (or its absence) and the same token cap. Only the first example gives you all three: its prompt is printed in full, its system message is quoted above it, and its 512-token cap is stated. The other three are described rather than reproduced — for the S3 and Postgres cases we show what was given in prose but not the literal configuration, and for the SQL-injection case we show the model's answer but never the vulnerable function it was asked about. You cannot reproduce those three from this card, and saying otherwise would be the same overclaim in a smaller form.
Model at a glance
| Property | Value |
|---|---|
| Model | Vinci-Cyber-8B-1.0 |
| Developer of this adaptation | Vinci / SimpleDirect, Canada |
| Parent | ibm-granite/granite-4.1-8b |
| Parent revision | 1504002f650e656a0a3789d99574df12e3e94ed0 |
| Architecture | GraniteForCausalLM |
| Parameters | 8,380,551,168 — approximately 8.38B |
| Stored tensors | 362, all BF16 |
| Distribution | Full merged weights in model.safetensors, not an adapter-only release |
| Weight-file size | 16,761,144,816 bytes — approximately 16.76 GB / 15.61 GiB |
| Licence for released weights | Apache-2.0 |
| Claims reviewed as of | September 21, 2026 |
| Latest measurement reported | September 20, 2026 (general-capability matrix) |
Weight-file size is not a runtime-memory requirement. Model loading, attention state, context length, batching, and the inference runtime require additional memory. This card does not claim that BF16 inference fits in 16 GB of GPU memory.
Should you use this model?
An honest decision table. The left column is what this model was built for; the right is where something else will serve you better.
| Reach for this model when… | Use something else when… |
|---|---|
| You already have a scanner finding and the file that triggered it, and you want a proposed minimal edit | You need to find the vulnerability — this is not a scanner and does not replace one |
| A human reviews every change before it is applied | You want unattended remediation that writes to production |
| Your configurations cannot leave your infrastructure — it runs entirely on your own hardware, with no hosted API | You are comfortable with a hosted frontier model and do not need local control |
| You want a small, auditable, open-weight model you can pin, diff and re-run indefinitely | You need breadth across many security domains |
| The work is infrastructure-as-code: Terraform / OpenTofu configuration | The work is application source code — see the out-of-scope SQL-injection example above |
| You want the weights, not a black box: full merged safetensors under Apache-2.0 | You need a certified or accredited product |
A realistic place it fits. A scanner runs in CI and opens a finding. This model drafts the minimal patch as a suggestion on the pull request. An engineer reads it, the existing test and policy gates run against it, and a human approves the merge. The model shortens the gap between "a scanner told me something is wrong" and "here is a candidate diff" — it does not close the loop, and it is not meant to.
Practical sizing. The weights are 16.76 GB in bf16, so the file alone will not fit in a 16 GB accelerator, and the runtime needs headroom beyond the weights for activations, KV cache and your context length. Quantized GGUF builds exist in a companion repository for smaller hardware; note that those are unmeasured — see the GGUF card for why — so validate any tier on your own cases before relying on it.
Intended use
Conceptual intended workflow, not a measured reliability result or an autonomous deployment loop. Validate and review both proposed changes and no-change decisions.
The intended application is a proposal-and-review workflow for defensive infrastructure security: provide relevant configuration and diagnostic context, request a proposed change or a no-change response, and independently check the result before applying it.
The repair, no-change, and recovery categories describe the training objective. They do not establish that the model reliably recognizes every vulnerability, preserves every legitimate configuration, or corrects its own mistakes.
Evaluate on systems you own or are authorized to assess. Keep configuration parsing, security checks, regression tests, and approval outside the model’s control. A persuasive explanation or a cleared warning is not, by itself, evidence that a patch is correct.
Evaluation and limitations of the evidence
Everything below was measured on this exact model, the 16.76 GB bfloat16 weights in this repository. Two things that sound pedantic but change what you can conclude:
- The GGUF files are a different artifact. None of these numbers were measured on them. A quantized copy may behave differently, and we have not checked how much.
- We publish the unflattering results too. Where a result went against us, it is below in the same detail as the ones that did not. Where we do not have enough evidence to claim something, we say so rather than leaving a gap that reads like a claim.
PatchEval-Verified: no supported broad repair improvement
Two populations are being compared here, and keeping them apart is the whole point.
| parent | this model | |
|---|---|---|
| passed, all 230 cases | 20 | 23 |
| pass@1 | 8.70% | 10.00% |
| Wilson 95% interval | 5.70 – 13.05% | 6.76 – 14.56% |
| converted into a scoreable patch | 135 | 153 |
| passed, on the 99 cases both converted | 16 | 16 |
| paired test | discordant pairs | exact McNemar |
|---|---|---|
| matched population, n = 99 | 7 vs 7 | p = 1.0000 |
| full population, n = 230 | 11 vs 14 | p = 0.6900 |
Read it in that order. The raw result moved by +3 passes — state that plainly. But it arrived alongside +18 conversions: this model turned more outputs into patches a scorer could read. On the cases where both models produced a scoreable patch, they each passed 16 — the same number, not the same cases. The two sets overlap on 9, with 7 cases passed only by the parent and 7 only by this model. The 95% intervals overlap heavily and both paired tests are far from significant.
So the nominal lead is a formatting and conversion effect, not a demonstrated repair-capability improvement. This evaluation does not support a broad code-repair or general CVE-repair claim. It equally does not prove the two models are equally capable — a null result is not evidence of no difference.
What that looks like case by case
The aggregate is null, but the individual cases are real CVEs in real projects, and they show what the model does on code it has never seen. Read them as illustrations of behaviour, not as evidence of improvement — the reason there is no improvement claim is visible in the numbers right here: across all 230 cases this model passed 14 the parent failed, and the parent passed 11 this model failed. Roughly one trade for every gain. Four of the five below are ours; there are eleven going the other way.
Authentication bypass — Prometheus exporter-toolkit, Go. When a username was not in the user
list, the code substituted a fixed bcrypt hash of the literal string "fakepassword" — a
constant-time trick to prevent username enumeration — and then carried on into the password check.
Anyone could authenticate as a non-existent user by sending the password fakepassword. The model
removed the fallback and rejected the request outright:
- // This is a bcrypt-hashed version of "fakepassword".
- hashedPassword = "$2y$10$QOauhQNbBCuQDKes6eFzPeMqBSjb7Mr5DUmpZ/VcEd00UAV/LDeSi"
+ // Do not provide a default hashed password to prevent bypassing authentication.
+ w.Header().Set("WWW-Authenticate", "Basic")
+ http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
+ return
Open redirect — oauth2-proxy, Go. The allowed-redirect-domain check used a bare suffix test, so
a wildcard entry for .example.com also matched the attacker-owned evilexample.com. One token, and
it is the shape of the real upstream fix — require the dot separator:
- strings.HasSuffix(redirectHostname, domainHostname)
+ strings.HasSuffix(redirectHostname, "."+domainHostname)
SQL injection — Django. order_by() accepted raw "table.column" aliases, emitted a deprecation
warning, and continued — so untrusted input reaching order_by() was interpolated into SQL. The model
turned the warning into a refusal. Path traversal — gitjacker, Go. Paths taken from a remote
repository were joined onto the local output directory, and a leading / let the write escape it;
the model stripped the separator before joining.
One the parent also fixed, included because omitting it would be selective: a Gradio access-control check compared paths case-sensitively, so on a case-insensitive filesystem a differently-cased spelling of a blocked path reached the file anyway. Both models normalised the comparison. That is capability, not improvement.
And two it got wrong, which are more instructive
A patch that would not build. For a Consul string-escaping fix the model wrote a correct-looking
strings.ReplaceAll(...) — and never added the import. Build output: undefined: strings. The
SEARCH/REPLACE format shows the model one window of the file; it cannot see the import block, so it
cannot add to it. That is a recurring shape of failure, not a one-off.
A patch that built and did not fix the bug. In requests, the model found the right file, the
right function, and the exact line that strips the Authorization header when a redirect crosses to
a different host — then added a condition that makes the stripping less likely to fire:
- if (original_parsed.hostname != redirect_parsed.hostname):
+ if (original_parsed.hostname != redirect_parsed.hostname) and response.is_redirect:
It imports cleanly, it runs, and the credential still leaks over plaintext HTTP. Correct localisation, wrong semantics — and nothing but the test caught it.
The largest failure mode is not a wrong fix at all. The model is asked to quote the existing lines it wants to change and then give the replacement; the tool finds that quoted text in the real file and swaps it. If the quote does not match the file character for character, there is nothing to anchor to and no patch is produced — the case is never scored. This happened on 49 of 230 cases for this model and 79 of 230 for the parent, and that gap is the single clearest measured difference between them. It says the model reproduces existing code more faithfully. It says nothing about whether it reasons about vulnerabilities better.
🔴 One caveat on the passes themselves. A case counts as passed when the proof-of-concept exploit stops reproducing and the project's tests still pass. Reviewing the patches, at least three of the 23 call a function they never define — a patch can satisfy that criterion by making the vulnerable path throw rather than by repairing it. We have not excluded those from the count, because we could not confirm it without the repositories; we are telling you the count has that softness in it.
These findings must be read together. The model produced more scoreable patch representations; it did not repair more cases in the population that both models successfully converted. This evaluation does not support a broad code-repair or general CVE-repair improvement claim. It also does not prove that the models have equal capability.
Instrument limitation: the corrected converter received predecessor and change-focused reviews, not a complete independent review of the final converter as a whole. Exact conversion counts remain exposed to unresolved converter limitations. These figures are disclosed as a non-claim about broader repair performance, not presented as a fully qualified benchmark win.
General coding: an internal paired retention check
| HumanEval-164, same evaluation job | Problems passed |
|---|---|
| Parent | 144 / 164 |
| Selected candidate, RC1 Seed B, checkpoint 18 | 140 / 164 |
| Difference from that same-job parent | −4 problems |
The comparison is from a single internal evaluation job (job_9e374f21 — an internal identifier, recorded for lineage, not for external lookup). It used a bespoke instruction wrapper, the parent chat template, a custom extractor, executed tests, and single-sample greedy decoding. These absolute scores are internal and prompt-specific; they are not comparable with published HumanEval leaderboard scores.
This is a coding-retention check with an observed decline, not a coding gain, a claim of unchanged coding ability, or a general guarantee of capability preservation. Parent scores from other evaluation jobs must not be substituted into this pairing.
What the internal infrastructure panel actually asks the model to do
The scores are below. The task design comes first, because it is what decides whether those scores mean anything: a repair count is only as strong as the definition of "repair" behind it.
Each repair case presents a real infrastructure-as-code file with a real misconfiguration that a real scanner flags. A response counts as a valid repair only if all five of these hold:
| condition | |
|---|---|
| C1 | the output parses under the required edit format |
| C2 | the patch applies cleanly to the file |
| C3 | the patched file survives tofu init -backend=false + tofu validate with no severity=error diagnostic |
| C4 | the target scanner fires on the unpatched baseline |
| C5 | the target scanner clears on the patched file |
C4 and C5 are the pair that matters. The scanner must genuinely flag the original and genuinely accept the fix — so a plausible-looking explanation earns nothing, and neither does an edit that merely looks like a patch. C3 means the result still has to be valid configuration, not just scanner-silent.
Alongside the repair cases the panel runs clean-file controls: files with nothing wrong, where the correct answer is to change nothing. These exist because the cheapest way to clear a finding is to delete the resource, zero its instance count, or blanket-deny it — edits that satisfy a scanner and destroy the infrastructure. Restraint is scored, not assumed.
🔴 The panel is small — 8 scoreable repair cases and 22 clean-file controls. That is a characterization panel, not a broad benchmark, and it is one reason no capability rate from it is published here.
Infrastructure-as-code and GGUF capability claims
On the internal panel of 8 scoreable repair cases, this model produced 4 valid verified repairs; the parent produced 0. On the 22 clean-file controls — files with nothing to fix — it correctly proposed no edit 22 times out of 22, against the parent's 18 of 22. This model's own figures reproduced unchanged across three independent greedy-decoding repeats.
BF16 source-model development evidence from the checkpoint-selection panel, not an untouched holdout. The scorer is not independently qualified; these results do not transfer to GGUF artifacts.
🔴 Read the parent's 0 as an output-contract failure, not a reasoning failure. On this panel the parent produced a parseable, appliable edit on 1 of 13 cases. A model that cannot emit an applicable edit scores zero whatever it understands, so the gap between 4 and 0 measures answer discipline first. We did not run a format-normalised re-score of this 8B parent, so its format-normalised floor is unmeasured and is above 0 — we cannot tell you by how much. For scale, the 30B parent, re-scored that way on the same panel, recovers 2 of 8; that number belongs to a different model and is not transferable here. Treat "4 against 0" as bounded by an unmeasured baseline.
We are publishing those numbers with three caveats, and the third is the one we would most want a sceptical reader to see.
First, the panel chose the checkpoint. It is a regression and characterization panel, not an untouched holdout. A number measured on the set you selected against is a development number, and comparing it to a held-out benchmark would be a mistake.
Second, the scoring instrument is not independently qualified. It failed its own qualification review on two counts, both of which would inflate a score rather than deflate it: it does not check whether a patch introduces a new insecure resource, and it has no availability-regression check, so deleting a resource outright would score as a clean repair. We audited all four passing patches against both defects. Neither occurred — every applied patch is a one- or two-line change to a single attribute inside the resource that was already there, nothing added and nothing removed, identical across all three repeats. The defects are real; they had nothing to act on here.
Third — and we found this while checking the other two — one of the four is weaker than "valid repair" suggests. Here is every one of them, so you can judge rather than take the count:
| case | what the model changed |
|---|---|
| unencrypted EBS volume | encrypted = false → true |
| Azure NSG open to the world | source_address_prefix = "*" → "10.0.0.0/16" |
| Cloud SQL open to the world | authorized network 0.0.0.0/0 → 10.0.0.0/8 |
| AWS security group, open egress | from_port = 0 → 80 |
That last one clears the scanner's check, and it does not fix the problem. It leaves protocol = "-1", to_port = 0 and cidr_blocks = ["0.0.0.0/0"] in place — and with protocol = "-1" AWS ignores the port range entirely, so egress to the whole internet on every protocol is exactly as open as before. from_port = 80 with to_port = 0 is also an invalid range that would likely be rejected on apply. The scanner was satisfied; the security property was not restored. Our scorer did not catch this, and neither of the two known defects above describes it — it is a third failure mode, and we are reporting it because it sits inside the 4.
A fifth case cleared the checker and we did not count it: the model replaced a hardcoded database password with var.db_password without declaring that variable, so the configuration no longer parses. That is why the number is 4 and not 5.
What we take from this. The direction is real, and restraint held on every clean file — 22 of 22, where the parent wrongly edited 4 of them. But the size of the repair gain is not: against an unmeasured format-normalised baseline, "4 against 0" is the weakest part of this result, not the strongest. And "4 of 8" contains at least one case that a careful human reviewer would reject, which is the entire argument for keeping a human in the loop, and is why every recommendation on this card assumes one. The restraint number is the one we would stand behind.
🔴 The historical GGUF per-tier figures are withheld for a different and independent reason, and scorer qualification would not resolve it. The evaluation drove llama-server, which emits no startup banner, so the llama.cpp runtime/build identity that produced those numbers was never captured; no run identifier was minted for the evaluation either. A publishable per-tier score has to bind seven things — artifact digest, run identifier, run date, evaluator digest, inference-runtime version, prompt-set digest, and the result. Five are bound for all four tiers. These two are not. They therefore do not meet this release's provenance requirements and were withdrawn rather than published with that gap. Treat the current quantized artifacts as unmeasured until they are evaluated under a fully captured runtime. This is a provenance defect, not a statement that the artifacts are poor or that the old numbers were false.
The internal infrastructure panel informed checkpoint selection. It is a regression and characterization panel, not an untouched generalization holdout. Independent scorer qualification would not, by itself, change that distinction.
GGUF artifacts may be distributed subject to their normal artifact gates while performance claims remain withheld. Artifact availability is not evidence of a particular tier’s quality or parity.
CyberSOCEval: this model scores below the parent it was trained from
This is the least flattering result in the release, and it is the one most worth reading.
CyberSOCEval is Meta's open defensive-security benchmark covering malware analysis and threat-intelligence reasoning. We ran the parent and this model twice each, on the same 608 items, through the same frozen harness, with greedy decoding (temperature=0.0, do_sample=False).
| run | correct | incorrect | unparsed | wrong format | score |
|---|---|---|---|---|---|
| Granite parent, run 1 | 148 | 410 | 41 | 9 | 24.3% |
| Granite parent, run 2 | 148 | 407 | 45 | 8 | 24.3% |
| Vinci-Cyber-8B, run 1 | 126 | 465 | 15 | 2 | 20.7% |
| Vinci-Cyber-8B, run 2 | 133 | 459 | 14 | 2 | 21.9% |
Two runs each, not a fraction. Every row sums to 608.
Our fine-tuning did not make this model better at this benchmark. It made it worse. Roughly three points of accuracy, in the same direction on every run and under every way we sliced it.
What did improve is how reliably it answers in the requested format. Unparseable answers fell from 41–45 to 14–15, and truncated answers from 34–35 to 9–13. (Truncation is measured separately from the four columns above and does not appear in them — a truncated answer can still land in any of the four buckets, so the two sets of figures are not additive.) In percentage terms, 91.8% / 91.3% of the parent's answers were readable, against 97.2% / 97.4% of this model's.
That raises an obvious question, so we tested it: if the model is only "losing" because the parent's answers were unreadable, then scoring just the readable answers should close the gap. It does not. On readable answers only, the parent scores 26.5% / 26.7% and this model 21.3% / 22.5% — still four to five points behind, on exactly the population where formatting cannot be the explanation.
So the honest summary is: the fine-tuning bought answer discipline, not security reasoning.
Two limits on how precisely to read any of this:
- The gap is not statistically significant. Restricting to items each model answers consistently across its own two runs, an exact McNemar test gives p = 0.25. The direction is consistent; the size is not established. We are not publishing a significance claim in either direction.
- Greedy decoding was not reproducible here, and that bounds every number above. Same settings, same weights, same benchmark — the parent scored exactly 148 both times, yet 42 of the 608 items changed correctness between those two runs, 21 in each direction, cancelling out. An unchanged total looked like perfect stability and was not. Treat a three-point difference as a rough signal, not a precise measurement.
The parent's identity was confirmed by hashing the base weight files directly rather than trusting a directory name, after we noticed the two arms had loaded paths with different repository names.
General capability against the base model
The sections above ask whether this model is better at defensive security work. This section asks a
different question: did the cybersecurity fine-tune change general capability relative to
ibm-granite/granite-4.1-8b? It redistributed it: two confirmed regressions, and four
differences in the other direction of which — as the analysis below sets out — none is an
established gain.
Measured with lm-evaluation-harness 0.4.11, 0-shot, seed 0, bfloat16, batch size 8, on a single
H200. Subject snapshot 1b768e4bf796f6c9ade43d7407b772384f3eab0c; base
ibm-granite/granite-4.1-8b. Every cell was run two to four times on both models, so each
difference is judged against the run-to-run variation of that exact pair rather than an assumed one.
| Benchmark | Metric | Vinci-Cyber-8B-1.0 | granite-4.1-8b | Difference |
|---|---|---|---|---|
| ARC-Challenge | acc_norm | 0.6109 | 0.5998 | +0.0111 |
| WinoGrande | acc | 0.7388 | 0.7348 | +0.0036 |
| MMLU | acc | 0.7191 | 0.7177 | +0.0014 |
| PIQA | acc_norm | 0.7965 | 0.7954 | +0.0011 |
| HellaSwag | acc_norm | 0.8125 | 0.8152 | −0.0027 |
| GSM8K (5-shot) | exact_match, strict-match | 0.8915 | 0.9017 | −0.0102 |
The GSM8K figures are the mean of six runs per arm; the other five rows are representative single values from their repeat sets. GSM8K was run 5-shot, the other five tasks 0-shot.
HellaSwag is a confirmed regression. It is roughly 30 items of 10,042. A pre-registered confirmatory test on the 39,905-item HellaSwag training split — four times the evaluation pool, and registered before the run — reproduced it at −0.32 percentage points, p = 5.2e-09, with a paired 90% interval of [−0.41, −0.23]. A negative control registered alongside it, the same test applied to the 30B pair on the identical items, stayed null, so the procedure is not manufacturing the effect.
Neither ARC-Challenge nor MMLU is an established gain. A paired item-level test — exact McNemar over the items both models answered, aligned per question — put ARC-Challenge at p = 0.019 across 1,172 items and MMLU at p = 0.28 across 14,042 items. A pre-registered confirmatory test on 1,418 held-out ARC-Challenge items then returned +0.56 percentage points at p = 0.20, with a 90% interval spanning zero: the exploratory estimate had been selected because it was large, and it halved on fresh items. ARC-Challenge is therefore a direction, not a demonstrated gain, and MMLU is not a gain at all. Run-to-run reproduction is not evidence of a real difference here: under greedy decoding on a fixed item set, every arm reproduces its score exactly whatever its true accuracy, so identical repeat runs measure the harness, not the models.
PIQA is marginal. A 2-item difference against a 1-item run-to-run spread. It is reported for completeness and should not be leaned on.
GSM8K is the second regression. 5-shot exact_match strict-match was run six times on each
model. This model scored 0.8969, 0.8878, 0.8893, 0.8908, 0.8939 and 0.8901 — a range of 0.8878 to
0.8969. The base scored 0.9014, 0.8999, 0.9037, 0.9007, 0.9037 and 0.9007 — a range of 0.8999 to
0.9037. Every run of this model is below every run of the base, and the mean gap is 0.0102, roughly
13 items of 1,319. A paired item-level test over the 1,319 questions both models answered — exact
McNemar, aligned per question — gives 41 disagreements, 28 where the base is right and this model
wrong against 13 the other way, p = 0.027. That paired test, not the separation of the run ranges,
is what supports the regression; it does not survive correction for the twelve model-by-task
comparisons run across both model sizes.
Correction to an earlier revision of this section. That revision excluded GSM8K, on the grounds that this model's 12-item run-to-run spread across two runs was wider than its 10-item gap to the base. That test was wrong: one arm's spread is not the yardstick for the gap between arms. It was replaced by a comparison of whether the two arms' score ranges overlap, and six runs per arm show non-overlapping ranges. That replacement has since been superseded too — under greedy decoding repeated runs reproduce almost exactly, so non-overlapping ranges show reproducibility rather than a real difference. The paired test above is what now supports the regression, and GSM8K is recorded above as a regression rather than as an exclusion.
What this does and does not say. It is evidence about this checkpoint. The 30B sibling,
Vinci-Cyber-30B-1.0, measured on the same contract, showed small gains on four benchmarks and no
regression — so these two models did not respond to Cyber training the same way, and neither result
should be read as a general property of the training recipe.
The full matrix and the per-task repeat runs are recorded internally in
getsimpledirect/vinci-gpu-research under model-scouting/eval-matrix-20260920/.
Training
Vinci applied supervised fine-tuning with DoRA and rank-stabilized LoRA (rsLoRA) to the pinned IBM Granite parent.
| Setting | Value |
|---|---|
| Specialization corpus | FROZEN-RC1c |
| Examples | 96 |
| Composition | 60 repair / 24 no-change / 12 recovery |
| Method | SFT only for this specialization; no RL, DPO, or continued pretraining |
| Adapter rank / alpha / dropout | 32 / 64 / 0.05 |
| Rank-stabilized scaling | alpha / sqrt(rank) ≈ 11.3137085 |
| Targets | q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj |
| Precision | BF16 training; FP32 merge accumulation; BF16 stored output |
| Training sequence limit | 8,192 tokens; not a validated inference-context claim |
| Effective batch size | 8 |
| Learning rate / schedule | 8 × 10⁻⁶ / cosine |
| Loss | Assistant-only |
| Selected checkpoint | RC1 Seed B, seed 73191, checkpoint 18 |
| Selected training dose | Step 18 of 24; 75% of the scheduled run, 1.5 epochs |
The 96 examples are the specialization corpus, not the parent model’s pretraining data. Individual training rows are not distributed. The corpus carries LicenseRef-Vinci-Private-Pilot; the Apache-2.0 licence for the released weights does not license the corpus. This is an open-weight release, not a claim that all training data or the complete training process is publicly reproducible.
Merged weights and numerical fidelity
The distributed model contains the selected adapter merged into the parent. The original adapter is retained privately for lineage; it is not the public loading path and must not be applied again on top of the merged weights.
🔴 The numerical-fidelity diagnostics below were not measured on the weights distributed here. They were run on a separate same-procedure reference rebuild of the merge, produced under a different pinned software stack. That reference build hashes to eb1da25c29679a561050a91e6a24df87219288dbee9f2c5400ceeb19fa62d3f1; the file you download hashes to d07a721863b6f6366aa559f5a42e19140f2d61285de9093911521adad5c5c8e7. They are not the same bytes. Every figure in this subsection is therefore evidence about the merge procedure under that tested stack, and is not a numerical-parity measurement of the distributed artifact.
On that reference build, the study reports matching top-1 predictions at all 2,562 tested positions (drawn from 4 prompts, teacher-forced) when both diagnostic paths used FP32. This is a finite diagnostic result, not proof of identical logits or equivalent behaviour on all inputs — and, as stated above, not a property of the shipped file. Under BF16 execution, on that same reference build, the merged and adapter paths disagreed on 2.6151% of tested top-1 choices, and the reference model’s own BF16-versus-FP32 comparison disagreed on 2.8493%.
A preregistered stop condition triggered in the earlier cross-precision comparison. The record retains that negative result and records an owner exception following the same-precision FP32 control. The original condition was not retroactively marked as passed. The exception does not establish exact BF16 equivalence, and the merge checks do not independently validate the underlying DoRA mathematics.
Loading and deployment
Use the full-weight repository with its accompanying configuration, tokenizer, and chat template. Do not add adapter_config.json or reapply the preserved adapter to these weights. Pin the exact published repository revision and your runtime versions when running evaluations.
The example below is a reference loading pattern, not a verified run of this repository revision. It requires PyTorch, Transformers with Granite support, and Accelerate. It does not establish network-download success, performance, deterministic output, or compatibility with every runtime version.
import os
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
# Set VINCI_MODEL_PATH to a downloaded snapshot for local loading.
model_id = os.environ.get("VINCI_MODEL_PATH", "simpledirect/Vinci-Cyber-8B-1.0")
# For Hub loading, set this to the exact commit you intend to evaluate.
revision = os.environ.get("VINCI_MODEL_REVISION")
load_options = {"trust_remote_code": False}
if revision:
load_options["revision"] = revision
tokenizer = AutoTokenizer.from_pretrained(model_id, **load_options)
if not tokenizer.chat_template:
raise RuntimeError("The release tokenizer must include its chat template.")
model = AutoModelForCausalLM.from_pretrained(
model_id,
dtype=torch.bfloat16,
device_map="auto",
**load_options,
).eval()
messages = [{
"role": "user",
"content": (
"Explain why an infrastructure configuration change should be "
"validated before deployment. Do not propose or apply a live change."
),
}]
inputs = tokenizer.apply_chat_template(
messages,
add_generation_prompt=True,
tokenize=True,
return_dict=True,
return_tensors="pt",
)
input_device = model.get_input_embeddings().weight.device
inputs = {key: value.to(input_device) for key, value in inputs.items()}
with torch.inference_mode():
output = model.generate(
**inputs,
max_new_tokens=256,
do_sample=False,
)
completion = output[0, inputs["input_ids"].shape[-1]:]
print(tokenizer.decode(completion, skip_special_tokens=True))
Greedy decoding does not guarantee identical text across hardware and execution kernels. Validate the exact model revision, runtime, and configuration you deploy. This card makes no claim that the loading example above was executed against this repository revision.
Safety and scope
Treat every proposed change as untrusted until independently reviewed and tested. The model may miss a vulnerability, change a safe configuration, invent an option, produce an invalid patch, or give an explanation inconsistent with the code. On the out-of-domain CVE-repair evaluation it also failed to terminate on 3 of 230 cases, emitting a single edit block repeatedly instead of stopping; the parent did so on none. That is a specialization-introduced failure mode, it is disclosed rather than filtered out of the numbers above, and it is the one that bites hardest if you run this model unattended in a loop — cap output length and detect repetition. It is not a vulnerability scanner, an incident-response service, or a substitute for accountable engineering judgment.
Recommended controls include a sandbox, least-privilege tool permissions, restricted network access, audit logs, rollback procedures, and explicit approval before changes reach production. Keep secrets and sensitive data out of unapproved workflows.
Broad vulnerability discovery, security-operations reliability, adversarial robustness, multilingual security performance, and long-context reliability are not established by the evidence reported here. Parent-model capabilities and benchmark scores do not automatically transfer to this derivative.
Self-hosting provides a deployment choice. It does not, by itself, establish secure operation, privacy, data residency, or regulatory compliance. No government certification or endorsement is implied.
Provenance and integrity
The full-weight file identified by this card has the following recorded SHA-256:
model.safetensors
d07a721863b6f6366aa559f5a42e19140f2d61285de9093911521adad5c5c8e7
Which files come from where, and the one that is not the parent's
Every tokenizer and configuration file in this repository is carried forward from the parent
unmodified — config.json, generation_config.json, tokenizer_config.json,
special_tokens_map.json, vocab.json, merges.txt, chat_template.jinja.
tokenizer.json is the exception, and the NOTICE file points here for the detail. It is the
training checkpoint's copy rather than the parent's, because it is the file the released evaluation
actually ran against. The two were compared field by field: model (all 100,352 vocabulary entries
and their merges), added_tokens (96), normalizer, pre_tokenizer, decoder, truncation,
padding and version are byte-identical. The only difference is post_processor — null in
the parent, and here a TemplateProcessing whose special-token map is empty and whose single and
pair templates pass the sequences through unchanged. That is an identity transform: it adds no
tokens and changes no ids. It is the one respect in which the file differs, and the respect in which
it is equivalent.
The pinned parent’s config.json has this recorded SHA-256. Matching a configuration file identifies that file; it is not a substitute for verifying model weights.
parent config.json
dea9d856cb57018117fe2fe3366f37cb4aa39424890061db2c0045a6a4efbda0
Additional lineage identifiers:
Selected adapter weights SHA-256
e21630066fd951e748428f0376b307ac8ce77f5bfcdc8baee020e6a97039107c
Specialization corpus SHA-256
e8b28320fdc805cdd415f23688daa515879a1408dfb2d0702bab12ff37b105b3
The audited source bundle and Hugging Face publication payload are different artifacts. Their directory-level roots are not interchangeable. HF-PUBLICATION-MANIFEST.json records the source-to-destination mapping; use the manifest applicable to the published revision. An edited README is not covered merely by retaining a predecessor’s approval or root digest.
Packaging integrity and the claims on this card were checked against the audited source bundle before release. That review is internal and is not offered here as something you can verify; it is also not a cybersecurity certification, and it does not extend to every subsequent repository revision. The trust anchor is unsigned; hashes support tamper detection against a trusted reference, not independent proof of authorship or a tamper-proof release.
Licence and attribution
The released weights are licensed under Apache-2.0, matching the parent’s declared licence. See the accompanying LICENSE and NOTICE. This is a modified derivative: Vinci fine-tuned the parent and merged the selected DoRA/rsLoRA adapter into the distributed weights.
For redistribution, include the licence, retain applicable notices, and preserve prominent modification notices as required by Apache-2.0. Preserve applicable upstream NOTICE material when present; do not invent an upstream notice. The intended-use and deployment guidance in this card does not add a field-of-use restriction to the licence.
IBM developed Granite; Vinci / SimpleDirect developed this adaptation. No affiliation with or endorsement by IBM or the Government of Canada is implied.
Parent: IBM Granite 4.1 8B. Licence terms: Apache License 2.0.
A Canadian-developed contribution to open-weight security tooling—available for independent evaluation, adaptation, and further research.
Citation
@misc{vinci-cyber-8b-1.0,
title = {Vinci-Cyber-8B-1.0: a defensive infrastructure-as-code repair model},
author = {{SimpleDirect}},
year = {2026},
note = {Open-weight specialization of IBM Granite 4.1 8B, revision 1504002f650e656a0a3789d99574df12e3e94ed0},
url = {https://huggingface.co/simpledirect/Vinci-Cyber-8B-1.0}
}
SimpleDirect is at getsimpledirect.com. If this model proposes a change that clears a check without actually fixing the problem, we want to know — we have already published one such case ourselves, in the infrastructure panel results above.
- Downloads last month
- 18


