Skip to content

feat(cloud): netcup adapter — adopt & install, since netcup has no order API - #968

Merged
ralyodio merged 1 commit into
masterfrom
worktree-cloud-netcup
Aug 16, 2026
Merged

feat(cloud): netcup adapter — adopt & install, since netcup has no order API#968
ralyodio merged 1 commit into
masterfrom
worktree-cloud-netcup

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Adds @profullstack/sh1pt-cloud-netcup, the 15th cloud adapter, driving netcup's SCP REST API.

netcup retired its SOAP webservice on 2026-04-30 and replaced it with a REST API at https://www.servercontrolpanel.de/scp-core/api/v1, OAuth2 via Keycloak, with a public OpenAPI spec. This adapter is written against that spec — all 63 endpoints were enumerated rather than inferred.

The thing that makes this adapter different

netcup has no order endpoint and no cancel endpoint. Servers are monthly contracts bought through checkout, not API resources. There is no POST /servers and no delete anywhere in the spec. So two verbs are redefined rather than faked:

  • provision adopts. It finds a server already on the account with no OS installed and installs one via POST /servers/{serverId}/image, which carries hostname, sshKeyIds and a first-boot customScript. One call can land a fully configured box.
  • destroy throws, naming the Customer Control Panel. Powering the server off would report success while the contract kept billing — a worse failure than an honest error.

Guardrails

Installing an image wipes the target disk, so adoption is deliberately timid:

  • never a server that already has a template (an OS is installed)
  • never a disabled server
  • never a guess between multiple candidates without an adoptPrefix configured

When nothing is adoptable, provision fails with the plan matching the spec and a link to buy it, then adopts it on the next run.

Notes

  • quote reads a price list compiled into the adapter — netcup publishes no pricing endpoint. netcup bills monthly; the hourly field is derived only to satisfy the Quote shape.
  • Both client_credentials and password grants are supported. The SCP userId is not the CCP customer number, which the README calls out.
  • The README is hand-written (the generator skips non-generated READMEs) because this adapter's semantics differ enough from the other fourteen to need explaining.

Testing

18 adapter tests covering auth, the adopt rules, the refusal paths, image selection, hostname normalization and error extraction. tsc --noEmit clean. The 26-test registry suite passes with netcup added.

Transport was smoke-tested against the live API without credentials: the token endpoint accepts the client_credentials grant shape, and /servers returns errors in a message field, which is what extractErrorMessage reads first.

🤖 Generated with Claude Code

…der API

netcup sells servers as monthly contracts through checkout, not as API
resources: the retired SOAP webservice had no order method and the REST API
that replaced it on 2026-04-30 has 63 endpoints, none of which create or
delete a server.

So provision adopts — it takes a server already on the account with no OS
installed and installs one via POST /servers/{id}/image, which carries
hostname, sshKeyIds and a first-boot customScript, so one call lands a fully
configured box. destroy throws rather than powering off, because reporting
success while the contract keeps billing is worse than failing.

Adoption is deliberately timid, since installing an image wipes the target
disk: never a server with a template, never a disabled one, and never a guess
between multiple candidates without an adoptPrefix.

18 adapter tests, registry test updated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

84 finding(s)

HIGH/CRITICAL: 24 | MEDIUM: 51 | LOW: 9

Severity Rule Location
HIGH secret-generic-api-key packages/affiliates/sovrn/src/index.ts:28
HIGH js-nosql-injection packages/ai/amazon-bedrock/src/index.test.ts:121
HIGH secret-generic-credential packages/ai/amazon-bedrock/src/index.ts:9
HIGH secret-generic-credential packages/ai/amazon-bedrock/src/index.ts:10
HIGH secret-generic-credential packages/ai/amazon-bedrock/src/index.ts:11
HIGH js-host-header-trust packages/bots/wechat/src/index.ts:405
HIGH secret-generic-credential packages/bridges/matrix/src/index.ts:58
HIGH secret-generic-credential packages/bridges/matrix/src/index.ts:59
HIGH secret-generic-credential packages/captcha/captchasolver/src/index.ts:34
HIGH secret-generic-credential packages/cli/src/commands/secrets.ts:176
HIGH secret-generic-credential packages/cloud/linode/src/index.ts:15
HIGH secret-generic-credential packages/observability/sentry/src/index.ts:15
HIGH secret-generic-credential packages/outreach/producthunt/src/index.ts:103
HIGH secret-generic-credential packages/promo/posthog/src/index.ts:23
HIGH secret-generic-credential packages/security/snyk/src/index.ts:26
HIGH secret-generic-credential packages/social/hashnode/src/index.ts:4
HIGH secret-generic-credential packages/social/linkedin/src/index.ts:3
HIGH secret-generic-credential packages/social/linkedin/src/index.ts:4
HIGH secret-generic-credential packages/social/medium/src/index.ts:4
HIGH secret-generic-credential packages/social/snapchat/src/index.ts:5
HIGH secret-generic-credential packages/social/tiktok/src/index.ts:5
HIGH secret-generic-credential packages/targets/registry-ans/src/index.ts:49
HIGH secret-generic-credential sites/sh1pt.com/supabase/config.toml:303
HIGH secret-generic-credential sites/sh1pt.com/supabase/config.toml:335
MEDIUM redos-nested-quantifier packages/actions-fleet-core/src/action-pack/schema.ts:3
MEDIUM insecure-temp-file packages/agent-providers/opencode/src/__tests__/opencode.test.ts:19
MEDIUM insecure-temp-file packages/agent-providers/opencode/src/__tests__/opencode.test.ts:42
MEDIUM insecure-temp-file packages/agent-providers/opencode/src/__tests__/opencode.test.ts:45
MEDIUM insecure-temp-file packages/bridges/signal/src/index.test.ts:92
MEDIUM insecure-temp-file packages/bridges/signal/src/index.test.ts:118
MEDIUM insecure-temp-file packages/cli/src/input.test.ts:84
MEDIUM redos-nested-quantifier packages/core/src/setup-helpers.ts:583
MEDIUM insecure-temp-file packages/core/src/testing/harness.ts:15
MEDIUM insecure-temp-file packages/core/src/testing/harness.ts:16
MEDIUM insecure-temp-file packages/core/src/testing/harness.ts:30
MEDIUM insecure-temp-file packages/core/src/testing/harness.ts:42
MEDIUM insecure-temp-file packages/merch/printful/src/index.test.ts:9
MEDIUM insecure-temp-file packages/merch/printify/src/index.test.ts:11
MEDIUM insecure-temp-file packages/policy/src/linter.test.ts:8
MEDIUM redos-nested-quantifier packages/policy/src/rules/bundle-id.ts:3
MEDIUM insecure-temp-file packages/secrets/env-updater/src/index.test.ts:106
MEDIUM insecure-temp-file packages/social/facebook/src/index.test.ts:95
MEDIUM insecure-temp-file packages/social/instagram/src/index.test.ts:177
MEDIUM insecure-temp-file packages/social/pinterest/src/index.test.ts:91
MEDIUM insecure-temp-file packages/social/pinterest/src/index.test.ts:146
MEDIUM insecure-temp-file packages/social/threads/src/index.test.ts:108
MEDIUM insecure-temp-file packages/social/vimeo/src/index.test.ts:137
MEDIUM insecure-temp-file packages/social/x/src/index.test.ts:72
MEDIUM insecure-temp-file packages/social/x/src/index.test.ts:116
MEDIUM insecure-temp-file packages/targets/browser-safari/src/index.test.ts:22

…and 34 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio marked this pull request as ready for review August 16, 2026 16:03
@ralyodio
ralyodio merged commit f779519 into master Aug 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant